diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt index 6bc16793e..e46158ac7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt @@ -59,9 +59,13 @@ class TaskAlarmManager( */ private fun setAlarmsForTask(task: Task) { CoroutineScope(Dispatchers.IO).launch { + val reminderOccurencesToSchedule = if (task.type == TaskType.TODO) { 1 } else { + // For dailies, we schedule multiple reminders in advance + upcomingReminderOccurrencesToSchedule + } task.reminders?.let { reminders -> for (reminder in reminders) { - val upcomingReminders = task.getNextReminderOccurrences(reminder, upcomingReminderOccurrencesToSchedule) + val upcomingReminders = task.getNextReminderOccurrences(reminder, reminderOccurencesToSchedule) upcomingReminders?.forEachIndexed { index, reminderNextOccurrenceTime -> reminder?.time = reminderNextOccurrenceTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) setAlarmForRemindersItem(task, reminder, index) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt index e6e7f47c6..cc020df93 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt @@ -354,6 +354,14 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask { if (remindersItem == null) return null val reminderTime = remindersItem.time?.parseToZonedDateTime() ?: return null + var dateTimeOccurenceToSchedule = ZonedDateTime.now().withZoneSameInstant(ZoneId.systemDefault()) + val occurrencesList = mutableListOf() + + // If the reminder is a todo, only schedule sole dueDate/time occurrence. Otherwise, schedule multiple occurrences in advance + if (this.type == TaskType.TODO) { + occurrencesList.add(this.dueDate?.toZonedDateTime()?.withHour(reminderTime.hour)?.withMinute(reminderTime.minute) ?: return null) + return occurrencesList + } val now = ZonedDateTime.now().withZoneSameInstant(ZoneId.systemDefault()) var startDate = this.startDate?.toInstant()?.atZone(ZoneId.systemDefault()) ?: return null val frequency = this.frequency ?: return null @@ -361,81 +369,74 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask { val repeatDays = this.repeat startDate = startDate.withHour(reminderTime.hour).withMinute(reminderTime.minute) - var dateTimeOccurenceToSchedule = ZonedDateTime.now().withZoneSameInstant(ZoneId.systemDefault()) - - val occurrencesList = mutableListOf() - while (occurrencesList.size < occurrences) { // Increment currentDate based on the frequency - try { - dateTimeOccurenceToSchedule = when (frequency) { - Frequency.DAILY -> { - dateTimeOccurenceToSchedule = if (dateTimeOccurenceToSchedule.isBefore(startDate)) { - startDate - } else { - dateTimeOccurenceToSchedule.plusDays(everyX.toLong()).withHour(reminderTime.hour).withMinute(reminderTime.minute) - } - dateTimeOccurenceToSchedule + dateTimeOccurenceToSchedule = when (frequency) { + Frequency.DAILY -> { + dateTimeOccurenceToSchedule = if (dateTimeOccurenceToSchedule.isBefore(startDate)) { + startDate + } else { + dateTimeOccurenceToSchedule.plusDays(everyX.toLong()).withHour(reminderTime.hour).withMinute(reminderTime.minute) } - Frequency.WEEKLY -> { - // Set to start date if current date is earlier - if (dateTimeOccurenceToSchedule.isBefore(startDate)) { - dateTimeOccurenceToSchedule = startDate - } else if (repeatDays?.hasAnyDaySelected() == true) { + dateTimeOccurenceToSchedule + } + Frequency.WEEKLY -> { + // Set to start date if current date is earlier + if (dateTimeOccurenceToSchedule.isBefore(startDate)) { + dateTimeOccurenceToSchedule = startDate + } else if (repeatDays?.hasAnyDaySelected() == true) { - var nextDueDate = dateTimeOccurenceToSchedule.withHour(reminderTime.hour).withMinute(reminderTime.minute) - // If the next due date already happened for today, increment it by one day. Otherwise, it will be scheduled for today. - if (nextDueDate.isBefore(now) && occurrencesList.size == 0) { - nextDueDate = nextDueDate.plusDays(1) - } + var nextDueDate = dateTimeOccurenceToSchedule.withHour(reminderTime.hour).withMinute(reminderTime.minute) + // If the next due date already happened for today, increment it by one day. Otherwise, it will be scheduled for today. + if (nextDueDate.isBefore(now) && occurrencesList.size == 0) { + nextDueDate = nextDueDate.plusDays(1) + } - // If the reminder being scheduled is not the first iteration of the reminder, increment it by one day - if (occurrencesList.size > 0) { - nextDueDate = nextDueDate.plusDays(1) - } + // If the reminder being scheduled is not the first iteration of the reminder, increment it by one day + if (occurrencesList.size > 0) { + nextDueDate = nextDueDate.plusDays(1) + } + while (!nextDueDate.matchesRepeatDays(repeatDays)) { + nextDueDate = nextDueDate.plusDays(1).withHour(reminderTime.hour).withMinute(reminderTime.minute) + } + // Calculate weeks since start and adjust for the correct interval + val weeksSinceStart = ChronoUnit.WEEKS.between(startDate.toLocalDate(), nextDueDate.toLocalDate()) + if (weeksSinceStart % everyX != 0L) { + val weeksToNextValidInterval = everyX - (weeksSinceStart % everyX) + nextDueDate = nextDueDate.plusWeeks(weeksToNextValidInterval) + // Find the exact next due day within the valid interval while (!nextDueDate.matchesRepeatDays(repeatDays)) { nextDueDate = nextDueDate.plusDays(1).withHour(reminderTime.hour).withMinute(reminderTime.minute) } - // Calculate weeks since start and adjust for the correct interval - val weeksSinceStart = ChronoUnit.WEEKS.between(startDate.toLocalDate(), nextDueDate.toLocalDate()) - if (everyX > 0 && weeksSinceStart % everyX != 0L) { - val weeksToNextValidInterval = everyX - (weeksSinceStart % everyX) - nextDueDate = nextDueDate.plusWeeks(weeksToNextValidInterval) - // Find the exact next due day within the valid interval - while (!nextDueDate.matchesRepeatDays(repeatDays)) { - nextDueDate = nextDueDate.plusDays(1).withHour(reminderTime.hour).withMinute(reminderTime.minute) - } - } + } - dateTimeOccurenceToSchedule = nextDueDate - } - // Set time to the reminder time - dateTimeOccurenceToSchedule = dateTimeOccurenceToSchedule.withHour(reminderTime.hour).withMinute(reminderTime.minute) - dateTimeOccurenceToSchedule - } - - Frequency.MONTHLY -> { - dateTimeOccurenceToSchedule = if (dateTimeOccurenceToSchedule.isBefore(startDate)) { - startDate - } else { - dateTimeOccurenceToSchedule.plusMonths(everyX.toLong()).withDayOfMonth(startDate.dayOfMonth).withHour(reminderTime.hour).withMinute(reminderTime.minute) - } - dateTimeOccurenceToSchedule - } - Frequency.YEARLY -> { - dateTimeOccurenceToSchedule = if (dateTimeOccurenceToSchedule.isBefore(startDate)) { - startDate - } else { - dateTimeOccurenceToSchedule.plusYears(everyX.toLong()).withDayOfMonth(startDate.dayOfMonth).withMonth(startDate.monthValue).withHour(reminderTime.hour).withMinute(reminderTime.minute) - } - dateTimeOccurenceToSchedule + dateTimeOccurenceToSchedule = nextDueDate } + // Set time to the reminder time + dateTimeOccurenceToSchedule = dateTimeOccurenceToSchedule.withHour(reminderTime.hour).withMinute(reminderTime.minute) + dateTimeOccurenceToSchedule + } + + Frequency.MONTHLY -> { + dateTimeOccurenceToSchedule = if (dateTimeOccurenceToSchedule.isBefore(startDate)) { + startDate + } else { + dateTimeOccurenceToSchedule.plusMonths(everyX.toLong()).withDayOfMonth(startDate.dayOfMonth).withHour(reminderTime.hour).withMinute(reminderTime.minute) + } + dateTimeOccurenceToSchedule + } + Frequency.YEARLY -> { + dateTimeOccurenceToSchedule = if (dateTimeOccurenceToSchedule.isBefore(startDate)) { + startDate + } else { + dateTimeOccurenceToSchedule.plusYears(everyX.toLong()).withDayOfMonth(startDate.dayOfMonth).withMonth(startDate.monthValue).withHour(reminderTime.hour).withMinute(reminderTime.minute) + } + dateTimeOccurenceToSchedule } - occurrencesList.add(dateTimeOccurenceToSchedule) - } catch (_: DateTimeException) { - // Invalid date like feb 30th } + + occurrencesList.add(dateTimeOccurenceToSchedule) }