mirror of https://github.com/iSoron/uhabits.git
parent
b21eb3f118
commit
8f5f72d9fd
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.core.reminders;
|
|
||||||
|
|
||||||
import androidx.annotation.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.core.*;
|
|
||||||
import org.isoron.uhabits.core.commands.*;
|
|
||||||
import org.isoron.uhabits.core.models.*;
|
|
||||||
import org.isoron.uhabits.core.preferences.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.inject.*;
|
|
||||||
|
|
||||||
import static org.isoron.uhabits.core.utils.DateUtils.*;
|
|
||||||
|
|
||||||
@AppScope
|
|
||||||
public class ReminderScheduler implements CommandRunner.Listener
|
|
||||||
{
|
|
||||||
private final WidgetPreferences widgetPreferences;
|
|
||||||
|
|
||||||
private CommandRunner commandRunner;
|
|
||||||
|
|
||||||
private HabitList habitList;
|
|
||||||
|
|
||||||
private SystemScheduler sys;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ReminderScheduler(@NonNull CommandRunner commandRunner,
|
|
||||||
@NonNull HabitList habitList,
|
|
||||||
@NonNull SystemScheduler sys,
|
|
||||||
@NonNull WidgetPreferences widgetPreferences)
|
|
||||||
{
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
this.habitList = habitList;
|
|
||||||
this.sys = sys;
|
|
||||||
this.widgetPreferences = widgetPreferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onCommandFinished(@Nullable Command command)
|
|
||||||
{
|
|
||||||
if (command instanceof CreateRepetitionCommand) return;
|
|
||||||
if (command instanceof ChangeHabitColorCommand) return;
|
|
||||||
scheduleAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void schedule(@NonNull Habit habit)
|
|
||||||
{
|
|
||||||
if (habit.getId() == null)
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "Habit has null id. Returning.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!habit.hasReminder())
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "habit=" + habit.getId() + " has no reminder. Skipping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long reminderTime = Objects.requireNonNull(habit.getReminder()).getTimeInMillis();
|
|
||||||
long snoozeReminderTime = widgetPreferences.getSnoozeTime(habit.getId());
|
|
||||||
|
|
||||||
if (snoozeReminderTime != 0)
|
|
||||||
{
|
|
||||||
long now = applyTimezone(getLocalTime());
|
|
||||||
sys.log("ReminderScheduler", String.format(
|
|
||||||
Locale.US,
|
|
||||||
"Habit %d has been snoozed until %d",
|
|
||||||
habit.getId(),
|
|
||||||
snoozeReminderTime));
|
|
||||||
|
|
||||||
if (snoozeReminderTime > now)
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "Snooze time is in the future. Accepting.");
|
|
||||||
reminderTime = snoozeReminderTime;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "Snooze time is in the past. Discarding.");
|
|
||||||
widgetPreferences.removeSnoozeTime(habit.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scheduleAtTime(habit, reminderTime);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void scheduleAtTime(@NonNull Habit habit, long reminderTime)
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "Scheduling alarm for habit=" + habit.getId());
|
|
||||||
|
|
||||||
if (!habit.hasReminder())
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "habit=" + habit.getId() + " has no reminder. Skipping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (habit.isArchived())
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "habit=" + habit.getId() + " is archived. Skipping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long timestamp = getStartOfDayWithOffset(removeTimezone(reminderTime));
|
|
||||||
sys.log("ReminderScheduler",
|
|
||||||
String.format(
|
|
||||||
Locale.US,
|
|
||||||
"reminderTime=%d removeTimezone=%d timestamp=%d",
|
|
||||||
reminderTime,
|
|
||||||
removeTimezone(reminderTime),
|
|
||||||
timestamp));
|
|
||||||
|
|
||||||
sys.scheduleShowReminder(reminderTime, habit, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void scheduleAll()
|
|
||||||
{
|
|
||||||
sys.log("ReminderScheduler", "Scheduling all alarms");
|
|
||||||
HabitList reminderHabits =
|
|
||||||
habitList.getFiltered(HabitMatcher.WITH_ALARM);
|
|
||||||
for (Habit habit : reminderHabits)
|
|
||||||
schedule(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void startListening()
|
|
||||||
{
|
|
||||||
commandRunner.addListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void stopListening()
|
|
||||||
{
|
|
||||||
commandRunner.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void snoozeReminder(Habit habit, long minutes)
|
|
||||||
{
|
|
||||||
long now = applyTimezone(getLocalTime());
|
|
||||||
long snoozedUntil = now + minutes * 60 * 1000;
|
|
||||||
widgetPreferences.setSnoozeTime(habit.getId(), snoozedUntil);
|
|
||||||
schedule(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface SystemScheduler
|
|
||||||
{
|
|
||||||
SchedulerResult scheduleShowReminder(long reminderTime, Habit habit, long timestamp);
|
|
||||||
|
|
||||||
SchedulerResult scheduleWidgetUpdate(long updateTime);
|
|
||||||
|
|
||||||
void log(String componentName, String msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SchedulerResult
|
|
||||||
{
|
|
||||||
IGNORED,
|
|
||||||
OK
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.core.reminders
|
||||||
|
|
||||||
|
import org.isoron.uhabits.core.AppScope
|
||||||
|
import org.isoron.uhabits.core.commands.ChangeHabitColorCommand
|
||||||
|
import org.isoron.uhabits.core.commands.Command
|
||||||
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
|
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
||||||
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
|
import org.isoron.uhabits.core.models.HabitMatcher
|
||||||
|
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.applyTimezone
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getLocalTime
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfDayWithOffset
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.removeTimezone
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.Objects
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AppScope
|
||||||
|
class ReminderScheduler @Inject constructor(
|
||||||
|
private val commandRunner: CommandRunner,
|
||||||
|
private val habitList: HabitList,
|
||||||
|
private val sys: SystemScheduler,
|
||||||
|
private val widgetPreferences: WidgetPreferences
|
||||||
|
) : CommandRunner.Listener {
|
||||||
|
@Synchronized
|
||||||
|
override fun onCommandFinished(command: Command) {
|
||||||
|
if (command is CreateRepetitionCommand) return
|
||||||
|
if (command is ChangeHabitColorCommand) return
|
||||||
|
scheduleAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun schedule(habit: Habit) {
|
||||||
|
if (habit.id == null) {
|
||||||
|
sys.log("ReminderScheduler", "Habit has null id. Returning.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!habit.hasReminder()) {
|
||||||
|
sys.log("ReminderScheduler", "habit=" + habit.id + " has no reminder. Skipping.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var reminderTime = Objects.requireNonNull(habit.reminder)!!.timeInMillis
|
||||||
|
val snoozeReminderTime = widgetPreferences.getSnoozeTime(habit.id!!)
|
||||||
|
if (snoozeReminderTime != 0L) {
|
||||||
|
val now = applyTimezone(getLocalTime())
|
||||||
|
sys.log(
|
||||||
|
"ReminderScheduler",
|
||||||
|
String.format(
|
||||||
|
Locale.US,
|
||||||
|
"Habit %d has been snoozed until %d",
|
||||||
|
habit.id,
|
||||||
|
snoozeReminderTime
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (snoozeReminderTime > now) {
|
||||||
|
sys.log("ReminderScheduler", "Snooze time is in the future. Accepting.")
|
||||||
|
reminderTime = snoozeReminderTime
|
||||||
|
} else {
|
||||||
|
sys.log("ReminderScheduler", "Snooze time is in the past. Discarding.")
|
||||||
|
widgetPreferences.removeSnoozeTime(habit.id!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scheduleAtTime(habit, reminderTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun scheduleAtTime(habit: Habit, reminderTime: Long) {
|
||||||
|
sys.log("ReminderScheduler", "Scheduling alarm for habit=" + habit.id)
|
||||||
|
if (!habit.hasReminder()) {
|
||||||
|
sys.log("ReminderScheduler", "habit=" + habit.id + " has no reminder. Skipping.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (habit.isArchived) {
|
||||||
|
sys.log("ReminderScheduler", "habit=" + habit.id + " is archived. Skipping.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val timestamp = getStartOfDayWithOffset(removeTimezone(reminderTime))
|
||||||
|
sys.log(
|
||||||
|
"ReminderScheduler",
|
||||||
|
String.format(
|
||||||
|
Locale.US,
|
||||||
|
"reminderTime=%d removeTimezone=%d timestamp=%d",
|
||||||
|
reminderTime,
|
||||||
|
removeTimezone(reminderTime),
|
||||||
|
timestamp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sys.scheduleShowReminder(reminderTime, habit, timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun scheduleAll() {
|
||||||
|
sys.log("ReminderScheduler", "Scheduling all alarms")
|
||||||
|
val reminderHabits = habitList.getFiltered(HabitMatcher.WITH_ALARM)
|
||||||
|
for (habit in reminderHabits) schedule(habit)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun startListening() {
|
||||||
|
commandRunner.addListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun stopListening() {
|
||||||
|
commandRunner.removeListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun snoozeReminder(habit: Habit, minutes: Long) {
|
||||||
|
val now = applyTimezone(getLocalTime())
|
||||||
|
val snoozedUntil = now + minutes * 60 * 1000
|
||||||
|
widgetPreferences.setSnoozeTime(habit.id, snoozedUntil)
|
||||||
|
schedule(habit)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SystemScheduler {
|
||||||
|
fun scheduleShowReminder(
|
||||||
|
reminderTime: Long,
|
||||||
|
habit: Habit,
|
||||||
|
timestamp: Long
|
||||||
|
): SchedulerResult
|
||||||
|
|
||||||
|
fun scheduleWidgetUpdate(updateTime: Long): SchedulerResult?
|
||||||
|
fun log(componentName: String?, msg: String?)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SchedulerResult {
|
||||||
|
IGNORED, OK
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue