Take timezone into account when scheduling alarms

pull/151/head
Alinson S. Xavier 9 years ago
parent 3a7f27755c
commit f101975320

@ -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,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)

@ -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)

Loading…
Cancel
Save