mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Take timezone into account when scheduling alarms
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class ListHabitsController
|
||||
if (prefs.isFirstRun()) onFirstRun();
|
||||
|
||||
new Handler().postDelayed(() -> taskRunner.execute(() -> {
|
||||
reminderScheduler.schedule(habitList);
|
||||
reminderScheduler.scheduleAll();
|
||||
widgetUpdater.updateWidgets();
|
||||
}), 1000);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,18 +222,18 @@ public abstract class DateUtils
|
||||
return number % 7;
|
||||
}
|
||||
|
||||
public static long removeTimezone(long timestamp)
|
||||
{
|
||||
TimeZone tz = getTimezone();
|
||||
long now = new Date(timestamp).getTime();
|
||||
return now + tz.getOffset(now);
|
||||
}
|
||||
|
||||
public static void setFixedLocalTime(Long timestamp)
|
||||
{
|
||||
fixedLocalTime = timestamp;
|
||||
}
|
||||
|
||||
public static long toLocalTime(long timestamp)
|
||||
{
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
long now = new Date(timestamp).getTime();
|
||||
return now + tz.getOffset(now);
|
||||
}
|
||||
|
||||
public static Long truncate(TruncateField field, long timestamp)
|
||||
{
|
||||
GregorianCalendar cal = DateUtils.getCalendar(timestamp);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user