diff --git a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java index 8c34c38c4..be58809c9 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java +++ b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java @@ -39,7 +39,9 @@ public class HabitsApplication extends Application private static AppComponent component; - private static WidgetUpdater widgetUpdater; + private WidgetUpdater widgetUpdater; + + private ReminderScheduler reminderScheduler; public AppComponent getComponent() { @@ -95,6 +97,9 @@ public class HabitsApplication extends Application widgetUpdater = component.getWidgetUpdater(); widgetUpdater.startListening(); + reminderScheduler = component.getReminderScheduler(); + reminderScheduler.startListening(); + DatabaseUtils.initializeActiveAndroid(); } @@ -103,6 +108,8 @@ public class HabitsApplication extends Application { HabitsApplication.context = null; ActiveAndroid.dispose(); + + reminderScheduler.stopListening(); widgetUpdater.stopListening(); super.onTerminate(); } diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java index 858dcbf5b..0b1d78d32 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsController.java @@ -184,7 +184,7 @@ public class ListHabitsController if (prefs.isFirstRun()) onFirstRun(); new Handler().postDelayed(() -> taskRunner.execute(() -> { - reminderScheduler.schedule(habitList); + reminderScheduler.scheduleAll(); widgetUpdater.updateWidgets(); }), 1000); } diff --git a/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index 11ec1552a..5cc763d36 100644 --- a/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java +++ b/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -34,8 +34,6 @@ import org.isoron.uhabits.models.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.utils.*; -import java.util.*; - /** * The Android BroadcastReceiver for Loop Habit Tracker. *
@@ -104,7 +102,7 @@ public class ReminderReceiver extends BroadcastReceiver protected void onActionBootCompleted() { - reminderScheduler.schedule(habits); + reminderScheduler.scheduleAll(); } protected void onActionShowReminder(Context context, Intent intent) @@ -196,7 +194,7 @@ public class ReminderReceiver extends BroadcastReceiver private void createReminderAlarmsDelayed() { new Handler().postDelayed(() -> { - reminderScheduler.schedule(habits); + reminderScheduler.scheduleAll(); }, 5000); } @@ -223,7 +221,7 @@ public class ReminderReceiver extends BroadcastReceiver if (habit != null) { - long reminderTime = new Date().getTime() + delayMinutes * 60 * 1000; + long reminderTime = DateUtils.getLocalTime() + delayMinutes * 60 * 1000; reminderScheduler.schedule(habit, reminderTime); } diff --git a/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java b/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java index 1e3f3a7db..67d5f8033 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java @@ -32,11 +32,20 @@ public abstract class DateUtils { private static Long fixedLocalTime = null; + private static TimeZone fixedTimeZone = null; + /** * Number of milliseconds in one day. */ public static long millisecondsInOneDay = 24 * 60 * 60 * 1000; + public static long applyTimezone(long localTimestamp) + { + TimeZone tz = getTimezone(); + long now = new Date(localTimestamp).getTime(); + return now - tz.getOffset(now); + } + public static String formatHeaderDate(GregorianCalendar day) { Locale locale = Locale.getDefault(); @@ -116,7 +125,7 @@ public abstract class DateUtils { if (fixedLocalTime != null) return fixedLocalTime; - TimeZone tz = TimeZone.getDefault(); + TimeZone tz = getTimezone(); long now = new Date().getTime(); return now + tz.getOffset(now); } @@ -183,6 +192,17 @@ public abstract class DateUtils return getCalendar(getStartOfToday()); } + public static TimeZone getTimezone() + { + if(fixedTimeZone != null) return fixedTimeZone; + return TimeZone.getDefault(); + } + + public static void setFixedTimeZone(TimeZone tz) + { + fixedTimeZone = tz; + } + public static int getWeekday(long timestamp) { GregorianCalendar day = getCalendar(timestamp); @@ -202,16 +222,16 @@ public abstract class DateUtils return number % 7; } - public static void setFixedLocalTime(Long timestamp) + public static long removeTimezone(long timestamp) { - fixedLocalTime = timestamp; + TimeZone tz = getTimezone(); + long now = new Date(timestamp).getTime(); + return now + tz.getOffset(now); } - public static long toLocalTime(long timestamp) + public static void setFixedLocalTime(Long timestamp) { - TimeZone tz = TimeZone.getDefault(); - long now = new Date(timestamp).getTime(); - return now + tz.getOffset(now); + fixedLocalTime = timestamp; } public static Long truncate(TruncateField field, long timestamp) diff --git a/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java b/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java index a45acf3fb..6423d3286 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java +++ b/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java @@ -23,6 +23,7 @@ import android.app.*; import android.support.annotation.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.commands.*; import org.isoron.uhabits.intents.*; import org.isoron.uhabits.models.*; @@ -33,7 +34,7 @@ import javax.inject.*; import static org.isoron.uhabits.utils.DateUtils.*; @Singleton -public class ReminderScheduler +public class ReminderScheduler implements CommandRunner.Listener { private final PendingIntentFactory pendingIntentFactory; @@ -41,14 +42,31 @@ public class ReminderScheduler private final HabitLogger logger; + private CommandRunner commandRunner; + + private HabitList habitList; + @Inject public ReminderScheduler(@NonNull PendingIntentFactory pendingIntentFactory, @NonNull IntentScheduler intentScheduler, - @NonNull HabitLogger logger) + @NonNull HabitLogger logger, + @NonNull CommandRunner commandRunner, + @NonNull HabitList habitList) { this.pendingIntentFactory = pendingIntentFactory; this.intentScheduler = intentScheduler; this.logger = logger; + this.commandRunner = commandRunner; + this.habitList = habitList; + } + + @Override + public void onCommandExecuted(@NonNull Command command, + @Nullable Long refreshKey) + { + if(command instanceof ToggleRepetitionCommand) return; + if(command instanceof ChangeHabitColorCommand) return; + scheduleAll(); } public void schedule(@NonNull Habit habit, @Nullable Long reminderTime) @@ -56,7 +74,7 @@ public class ReminderScheduler if (!habit.hasReminder()) return; Reminder reminder = habit.getReminder(); if (reminderTime == null) reminderTime = getReminderTime(reminder); - long timestamp = getStartOfDay(toLocalTime(reminderTime)); + long timestamp = getStartOfDay(removeTimezone(reminderTime)); PendingIntent intent = pendingIntentFactory.showReminder(habit, reminderTime, timestamp); @@ -64,13 +82,24 @@ public class ReminderScheduler logger.logReminderScheduled(habit, reminderTime); } - public void schedule(@NonNull HabitList habits) + public void scheduleAll() { - HabitList reminderHabits = habits.getFiltered(HabitMatcher.WITH_ALARM); + HabitList reminderHabits = + habitList.getFiltered(HabitMatcher.WITH_ALARM); for (Habit habit : reminderHabits) schedule(habit, null); } + public void startListening() + { + commandRunner.addListener(this); + } + + public void stopListening() + { + commandRunner.removeListener(this); + } + @NonNull private Long getReminderTime(@NonNull Reminder reminder) { @@ -82,6 +111,6 @@ public class ReminderScheduler if (DateUtils.getLocalTime() > time) time += AlarmManager.INTERVAL_DAY; - return time; + return applyTimezone(time); } } diff --git a/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java b/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java index a2be3189a..073ebd291 100644 --- a/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java +++ b/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java @@ -22,10 +22,13 @@ package org.isoron.uhabits.utils; import android.app.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.commands.*; import org.isoron.uhabits.intents.*; import org.isoron.uhabits.models.*; import org.junit.*; +import java.util.*; + import static org.mockito.Mockito.*; @SuppressWarnings("JavaDoc") @@ -43,6 +46,8 @@ public class ReminderSchedulerTest extends BaseUnitTest private IntentScheduler intentScheduler; + private CommandRunner commandRunner; + @Before @Override public void setUp() @@ -52,18 +57,21 @@ public class ReminderSchedulerTest extends BaseUnitTest logger = mock(HabitLogger.class); pendingIntentFactory = mock(PendingIntentFactory.class); intentScheduler = mock(IntentScheduler.class); + commandRunner = mock(CommandRunner.class); reminderScheduler = - new ReminderScheduler(pendingIntentFactory, intentScheduler, - logger); + new ReminderScheduler(pendingIntentFactory, intentScheduler, logger, + commandRunner, habitList); habit = fixtures.createEmptyHabit(); + + DateUtils.setFixedTimeZone(TimeZone.getTimeZone("GMT-4")); } @Test public void testSchedule_atSpecificTime() { - long atTime = 1422617400000L; // 11:30 jan 30, 2015 (UTC) - long expectedCheckmarkTime = 1422576000000L; // 00:00 jan 27, 2015 (UTC) + long atTime = timestamp(2015, 1, 30, 11, 30); + long expectedCheckmarkTime = timestamp(2015, 1, 30, 0, 0); habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); scheduleAndVerify(atTime, expectedCheckmarkTime, atTime); @@ -72,11 +80,11 @@ public class ReminderSchedulerTest extends BaseUnitTest @Test public void testSchedule_laterToday() { - long now = 1422253800000L; // 06:30 jan 26, 2015 (UTC) + long now = timestamp(2015, 1, 26, 6, 30); DateUtils.setFixedLocalTime(now); - long expectedCheckmarkTime = 1422230400000L; // 00:00 jan 26, 2015 (UTC) - long expectedReminderTime = 1422261000000L; // 08:30 jan 26, 2015 (UTC) + long expectedCheckmarkTime = timestamp(2015, 1, 26, 0, 0); + long expectedReminderTime = timestamp(2015, 1, 26, 12, 30); habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); @@ -86,7 +94,7 @@ public class ReminderSchedulerTest extends BaseUnitTest @Test public void testSchedule_list() { - long now = 1422277200000L; // 13:00 jan 26, 2015 (UTC) + long now = timestamp(2015, 1, 26, 13, 0); DateUtils.setFixedLocalTime(now); Habit h1 = fixtures.createEmptyHabit(); @@ -100,21 +108,21 @@ public class ReminderSchedulerTest extends BaseUnitTest Habit h3 = fixtures.createEmptyHabit(); habitList.add(h3); - reminderScheduler.schedule(habitList); + reminderScheduler.scheduleAll(); - verify(intentScheduler).schedule(1422347400000L, null); - verify(intentScheduler).schedule(1422297000000L, null); + verify(intentScheduler).schedule(timestamp(2015, 1, 27, 12, 30), null); + verify(intentScheduler).schedule(timestamp(2015, 1, 26, 22, 30), null); verifyNoMoreInteractions(intentScheduler); } @Test public void testSchedule_tomorrow() { - long now = 1453813200000L; // 13:00 jan 26, 2016 (UTC) + long now = timestamp(2015, 1, 26, 13, 0); DateUtils.setFixedLocalTime(now); - long expectedCheckmarkTime = 1453852800000L; // 00:00 jan 27, 2016 (UTC) - long expectedReminderTime = 1453883400000L; // 08:30 jan 27, 2016 (UTC) + long expectedCheckmarkTime = timestamp(2015, 1, 27, 0, 0); + long expectedReminderTime = timestamp(2015, 1, 27, 12, 30); habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); scheduleAndVerify(null, expectedCheckmarkTime, expectedReminderTime); @@ -127,6 +135,13 @@ public class ReminderSchedulerTest extends BaseUnitTest verifyZeroInteractions(intentScheduler); } + public long timestamp(int year, int month, int day, int hour, int minute) + { + Calendar cal = DateUtils.getStartOfTodayCalendar(); + cal.set(year, month, day, hour, minute); + return cal.getTimeInMillis(); + } + private void scheduleAndVerify(Long atTime, long expectedCheckmarkTime, long expectedReminderTime)