mirror of https://github.com/tasks/tasks
Merge remote-tracking branch 'upstream/master'
# Conflicts: # src/main/res/values/keys.xml # src/main/res/xml/preferences_gtasks.xmlgtask_note_sync
commit
0b194a6cf9
@ -1,6 +1,6 @@
|
|||||||
#Thu Jan 26 17:04:55 CST 2017
|
#Tue May 23 14:22:03 CDT 2017
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zip
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
package com.todoroo.astrid.reminders;
|
|
||||||
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.tasks.preferences.Preferences;
|
|
||||||
import org.tasks.time.DateTime;
|
|
||||||
|
|
||||||
import static android.support.test.InstrumentationRegistry.getContext;
|
|
||||||
import static android.support.test.InstrumentationRegistry.getTargetContext;
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.with;
|
|
||||||
import static com.todoroo.astrid.data.Task.NOTIFY_AT_DEADLINE;
|
|
||||||
import static com.todoroo.astrid.reminders.ReminderService.NO_ALARM;
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
|
||||||
import static org.tasks.makers.TaskMaker.DUE_DATE;
|
|
||||||
import static org.tasks.makers.TaskMaker.DUE_TIME;
|
|
||||||
import static org.tasks.makers.TaskMaker.REMINDERS;
|
|
||||||
import static org.tasks.makers.TaskMaker.REMINDER_LAST;
|
|
||||||
import static org.tasks.makers.TaskMaker.newTask;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class NotifyAtDeadlineTest {
|
|
||||||
|
|
||||||
private ReminderService reminderService;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
Preferences preferences = new Preferences(getTargetContext(), null);
|
|
||||||
reminderService = new ReminderService(getContext(), preferences, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoReminderWhenNoDueDate() {
|
|
||||||
Task task = newTask(with(REMINDERS, NOTIFY_AT_DEADLINE));
|
|
||||||
assertEquals(NO_ALARM, reminderService.calculateNextDueDateReminder(task));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoReminderWhenNotifyAtDeadlineFlagNotSet() {
|
|
||||||
Task task = newTask(with(DUE_TIME, new DateTime(2014, 1, 24, 19, 23)));
|
|
||||||
assertEquals(NO_ALARM, reminderService.calculateNextDueDateReminder(task));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testScheduleReminderAtDueTime() {
|
|
||||||
final DateTime dueDate = new DateTime(2014, 1, 24, 19, 23);
|
|
||||||
Task task = newTask(with(DUE_TIME, dueDate), with(REMINDERS, NOTIFY_AT_DEADLINE));
|
|
||||||
assertEquals(dueDate.plusSeconds(1).getMillis(), reminderService.calculateNextDueDateReminder(task));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testScheduleReminderAtDefaultDueTime() {
|
|
||||||
final DateTime dueDate = new DateTime(2015, 12, 29, 12, 0);
|
|
||||||
Task task = newTask(with(DUE_DATE, dueDate), with(REMINDERS, NOTIFY_AT_DEADLINE));
|
|
||||||
assertEquals(dueDate.withHourOfDay(18).getMillis(),
|
|
||||||
reminderService.calculateNextDueDateReminder(task));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoReminderIfAlreadyRemindedPastDueDate() {
|
|
||||||
final DateTime dueDate = new DateTime(2015, 12, 29, 19, 23);
|
|
||||||
Task task = newTask(
|
|
||||||
with(DUE_TIME, dueDate),
|
|
||||||
with(REMINDER_LAST, dueDate.plusSeconds(1)),
|
|
||||||
with(REMINDERS, NOTIFY_AT_DEADLINE));
|
|
||||||
assertEquals(NO_ALARM, reminderService.calculateNextDueDateReminder(task));
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,240 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.tasks.Freeze;
|
||||||
|
import org.tasks.Snippet;
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.todoroo.astrid.reminders.ReminderService.TYPE_DUE;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static org.mockito.AdditionalAnswers.returnsFirstArg;
|
||||||
|
import static org.mockito.Matchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class JobQueueTest {
|
||||||
|
|
||||||
|
private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
|
||||||
|
private static final String TAG = "test";
|
||||||
|
|
||||||
|
private JobQueue<Reminder> queue;
|
||||||
|
private JobManager jobManager;
|
||||||
|
private Preferences preferences;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
preferences = mock(Preferences.class);
|
||||||
|
when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg());
|
||||||
|
jobManager = mock(JobManager.class);
|
||||||
|
queue = new JobQueue<>(preferences, jobManager, TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
verifyNoMoreInteractions(jobManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoJobsAtSameTime() {
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
queue.add(new Reminder(2, 1, 0));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, 1);
|
||||||
|
|
||||||
|
assertEquals(2, queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rescheduleForFirstJob() {
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dontRescheduleForLaterJobs() {
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
queue.add(new Reminder(2, 2, 0));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rescheduleForNewerJob() {
|
||||||
|
queue.add(new Reminder(1, 2, 0));
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
|
||||||
|
InOrder order = inOrder(jobManager);
|
||||||
|
order.verify(jobManager).schedule(TAG, 2);
|
||||||
|
order.verify(jobManager).schedule(TAG, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rescheduleWhenCancelingOnlyJob() {
|
||||||
|
queue.add(new Reminder(1, 2, 0));
|
||||||
|
queue.cancel(1);
|
||||||
|
|
||||||
|
InOrder order = inOrder(jobManager);
|
||||||
|
order.verify(jobManager).schedule(TAG, 2);
|
||||||
|
order.verify(jobManager).cancel(TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rescheduleWhenCancelingFirstJob() {
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
queue.add(new Reminder(2, 2, 0));
|
||||||
|
|
||||||
|
queue.cancel(1);
|
||||||
|
|
||||||
|
InOrder order = inOrder(jobManager);
|
||||||
|
order.verify(jobManager).schedule(TAG, 1);
|
||||||
|
order.verify(jobManager).schedule(TAG, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dontRescheduleWhenCancelingLaterJob() {
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
queue.add(new Reminder(2, 2, 0));
|
||||||
|
|
||||||
|
queue.cancel(2);
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nextScheduledTimeIsZeroWhenQueueIsEmpty() {
|
||||||
|
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
|
||||||
|
|
||||||
|
assertEquals(0, queue.nextScheduledTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adjustNextScheduledTimeForQuietHours() {
|
||||||
|
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
|
||||||
|
queue.add(new Reminder(1, 1, 1));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, 1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void overdueJobsAreReturned() {
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
queue.add(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, now);
|
||||||
|
|
||||||
|
Freeze.freezeAt(now).thawAfter(new Snippet() {{
|
||||||
|
assertEquals(
|
||||||
|
singletonList(new Reminder(1, now, TYPE_DUE)),
|
||||||
|
queue.getOverdueJobs());
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoOverdueJobsAtSameTimeReturned() {
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
queue.add(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.add(new Reminder(2, now, TYPE_DUE));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, now);
|
||||||
|
|
||||||
|
Freeze.freezeAt(now).thawAfter(new Snippet() {{
|
||||||
|
assertEquals(
|
||||||
|
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now, TYPE_DUE)),
|
||||||
|
queue.getOverdueJobs());
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoOverdueJobsAtDifferentTimes() {
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
queue.add(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, now);
|
||||||
|
|
||||||
|
Freeze.freezeAt(now + 2 * ONE_MINUTE).thawAfter(new Snippet() {{
|
||||||
|
assertEquals(
|
||||||
|
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now + ONE_MINUTE, TYPE_DUE)),
|
||||||
|
queue.getOverdueJobs());
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void overdueJobsAreRemoved() {
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
queue.add(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, now);
|
||||||
|
|
||||||
|
Freeze.freezeAt(now).thawAfter(new Snippet() {{
|
||||||
|
queue.remove(new Reminder(1, now, TYPE_DUE));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
singletonList(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)),
|
||||||
|
queue.getJobs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleOverduePeriodsLapsed() {
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
queue.add(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
|
||||||
|
queue.add(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE));
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, now);
|
||||||
|
|
||||||
|
Freeze.freezeAt(now + ONE_MINUTE).thawAfter(new Snippet() {{
|
||||||
|
queue.remove(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.remove(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
singletonList(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE)),
|
||||||
|
queue.getJobs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clearShouldCancelExisting() {
|
||||||
|
queue.add(new Reminder(1, 1, 0));
|
||||||
|
|
||||||
|
queue.clear();
|
||||||
|
|
||||||
|
InOrder order = inOrder(jobManager);
|
||||||
|
order.verify(jobManager).schedule(TAG, 1);
|
||||||
|
order.verify(jobManager).cancel(TAG);
|
||||||
|
assertEquals(0, queue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ignoreInvalidCancel() {
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
queue.add(new Reminder(1, now, TYPE_DUE));
|
||||||
|
queue.cancel(2);
|
||||||
|
|
||||||
|
verify(jobManager).schedule(TAG, now);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.tasks.injection;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.tasks.locale.Locale;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
class Dagger {
|
||||||
|
|
||||||
|
private static final Object lock = new Object();
|
||||||
|
|
||||||
|
private static Dagger instance;
|
||||||
|
|
||||||
|
public static Dagger get(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new Dagger(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationComponent applicationComponent;
|
||||||
|
|
||||||
|
private Dagger(Context context) {
|
||||||
|
Context localeContext = context.getApplicationContext();
|
||||||
|
try {
|
||||||
|
localeContext = Locale.getInstance(localeContext)
|
||||||
|
.createConfigurationContext(localeContext);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Timber.e(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationComponent = DaggerApplicationComponent.builder()
|
||||||
|
.applicationModule(new ApplicationModule(localeContext))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationComponent getApplicationComponent() {
|
||||||
|
return applicationComponent;
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,37 @@
|
|||||||
package org.tasks.injection;
|
package org.tasks.injection;
|
||||||
|
|
||||||
|
import org.tasks.jobs.AlarmJob;
|
||||||
|
import org.tasks.jobs.BackupJob;
|
||||||
|
import org.tasks.jobs.MidnightRefreshJob;
|
||||||
|
import org.tasks.jobs.RefreshJob;
|
||||||
|
import org.tasks.jobs.ReminderJob;
|
||||||
import org.tasks.location.GeofenceTransitionsIntentService;
|
import org.tasks.location.GeofenceTransitionsIntentService;
|
||||||
import org.tasks.scheduling.BackupIntentService;
|
|
||||||
import org.tasks.scheduling.CalendarNotificationIntentService;
|
import org.tasks.scheduling.CalendarNotificationIntentService;
|
||||||
import org.tasks.scheduling.GeofenceSchedulingIntentService;
|
import org.tasks.scheduling.GeofenceSchedulingIntentService;
|
||||||
import org.tasks.scheduling.RefreshSchedulerIntentService;
|
import org.tasks.scheduling.NotificationSchedulerIntentService;
|
||||||
import org.tasks.scheduling.ReminderSchedulerIntentService;
|
import org.tasks.scheduling.SchedulerIntentService;
|
||||||
|
|
||||||
import dagger.Subcomponent;
|
import dagger.Subcomponent;
|
||||||
|
|
||||||
@Subcomponent(modules = IntentServiceModule.class)
|
@Subcomponent(modules = IntentServiceModule.class)
|
||||||
public interface IntentServiceComponent {
|
public interface IntentServiceComponent {
|
||||||
void inject(ReminderSchedulerIntentService reminderSchedulerIntentService);
|
void inject(SchedulerIntentService schedulerIntentService);
|
||||||
|
|
||||||
void inject(RefreshSchedulerIntentService refreshSchedulerIntentService);
|
|
||||||
|
|
||||||
void inject(GeofenceSchedulingIntentService geofenceSchedulingIntentService);
|
void inject(GeofenceSchedulingIntentService geofenceSchedulingIntentService);
|
||||||
|
|
||||||
void inject(CalendarNotificationIntentService calendarNotificationIntentService);
|
void inject(CalendarNotificationIntentService calendarNotificationIntentService);
|
||||||
|
|
||||||
void inject(BackupIntentService backupIntentService);
|
|
||||||
|
|
||||||
void inject(GeofenceTransitionsIntentService geofenceTransitionsIntentService);
|
void inject(GeofenceTransitionsIntentService geofenceTransitionsIntentService);
|
||||||
|
|
||||||
|
void inject(NotificationSchedulerIntentService notificationSchedulerIntentService);
|
||||||
|
|
||||||
|
void inject(AlarmJob alarmJob);
|
||||||
|
|
||||||
|
void inject(BackupJob backupJob);
|
||||||
|
|
||||||
|
void inject(MidnightRefreshJob midnightRefreshJob);
|
||||||
|
|
||||||
|
void inject(RefreshJob refreshJob);
|
||||||
|
|
||||||
|
void inject(ReminderJob reminderJob);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import com.todoroo.astrid.alarms.AlarmFields;
|
||||||
|
import com.todoroo.astrid.data.Metadata;
|
||||||
|
|
||||||
|
public class Alarm implements JobQueueEntry {
|
||||||
|
private final long alarmId;
|
||||||
|
private final long taskId;
|
||||||
|
private final long time;
|
||||||
|
|
||||||
|
public Alarm(Metadata metadata) {
|
||||||
|
this(metadata.getId(), metadata.getTask(), metadata.getValue(AlarmFields.TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Alarm(long alarmId, long taskId, Long time) {
|
||||||
|
this.alarmId = alarmId;
|
||||||
|
this.taskId = taskId;
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return alarmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Alarm alarm = (Alarm) o;
|
||||||
|
|
||||||
|
return alarmId == alarm.alarmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (int) (alarmId ^ (alarmId >>> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Alarm{" +
|
||||||
|
"alarmId=" + alarmId +
|
||||||
|
", taskId=" + taskId +
|
||||||
|
", time=" + time +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.todoroo.astrid.alarms.AlarmService;
|
||||||
|
import com.todoroo.astrid.dao.TaskDao;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
import com.todoroo.astrid.reminders.ReminderService;
|
||||||
|
|
||||||
|
import org.tasks.Notifier;
|
||||||
|
import org.tasks.injection.IntentServiceComponent;
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class AlarmJob extends WakefulJob {
|
||||||
|
|
||||||
|
public static final String TAG = "job_alarm";
|
||||||
|
|
||||||
|
@Inject Preferences preferences;
|
||||||
|
@Inject AlarmService alarmService;
|
||||||
|
@Inject Notifier notifier;
|
||||||
|
@Inject TaskDao taskDao;
|
||||||
|
|
||||||
|
public AlarmJob() {
|
||||||
|
super(AlarmJob.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
if (!preferences.isCurrentlyQuietHours()) {
|
||||||
|
for (Alarm alarm : alarmService.getOverdueAlarms()) {
|
||||||
|
Task task = taskDao.fetch(alarm.getTaskId(), Task.REMINDER_LAST);
|
||||||
|
if (task != null && task.getReminderLast() < alarm.getTime()) {
|
||||||
|
notifier.triggerTaskNotification(alarm.getTaskId(), ReminderService.TYPE_ALARM);
|
||||||
|
}
|
||||||
|
alarmService.remove(alarm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduleNext() {
|
||||||
|
alarmService.scheduleNextJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(IntentServiceComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completeWakefulIntent(Intent intent) {
|
||||||
|
AlarmJobBroadcast.completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||||
|
|
||||||
|
public class AlarmJobBroadcast extends WakefulBroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
startWakefulService(context, new Intent(context, AlarmJob.class));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import org.tasks.analytics.Tracker;
|
||||||
|
import org.tasks.injection.InjectingIntentService;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public abstract class Job extends InjectingIntentService {
|
||||||
|
|
||||||
|
@Inject Tracker tracker;
|
||||||
|
|
||||||
|
public Job(String name) {
|
||||||
|
super(name);
|
||||||
|
setIntentRedelivery(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
super.onHandleIntent(intent);
|
||||||
|
|
||||||
|
Timber.d("onHandleIntent(%s)", intent);
|
||||||
|
|
||||||
|
try {
|
||||||
|
run();
|
||||||
|
} catch (Exception e) {
|
||||||
|
tracker.reportException(e);
|
||||||
|
} finally {
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void run();
|
||||||
|
|
||||||
|
protected abstract void scheduleNext();
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import org.tasks.injection.ApplicationScope;
|
||||||
|
import org.tasks.injection.ForApplication;
|
||||||
|
import org.tasks.scheduling.AlarmManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
||||||
|
import static org.tasks.time.DateTimeUtils.nextMidnight;
|
||||||
|
import static org.tasks.time.DateTimeUtils.printTimestamp;
|
||||||
|
|
||||||
|
@ApplicationScope
|
||||||
|
public class JobManager {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private AlarmManager alarmManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JobManager(@ForApplication Context context, AlarmManager alarmManager) {
|
||||||
|
this.context = context;
|
||||||
|
this.alarmManager = alarmManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedule(String tag, long time) {
|
||||||
|
Timber.d("%s: %s", tag, printTimestamp(time));
|
||||||
|
alarmManager.wakeup(adjust(time), getPendingIntent(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleRefresh(long time) {
|
||||||
|
Timber.d("%s: %s", RefreshJob.TAG, printTimestamp(time));
|
||||||
|
alarmManager.noWakeup(adjust(time), getPendingService(RefreshJob.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleMidnightRefresh() {
|
||||||
|
long time = nextMidnight();
|
||||||
|
Timber.d("%s: %s", MidnightRefreshJob.TAG, printTimestamp(time));
|
||||||
|
alarmManager.noWakeup(adjust(time), getPendingService(MidnightRefreshJob.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleMidnightBackup() {
|
||||||
|
long time = nextMidnight();
|
||||||
|
Timber.d("%s: %s", BackupJob.TAG, printTimestamp(time));
|
||||||
|
alarmManager.noWakeup(adjust(time), getPendingService(BackupJob.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel(String tag) {
|
||||||
|
Timber.d("CXL %s", tag);
|
||||||
|
alarmManager.cancel(getPendingIntent(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private long adjust(long time) {
|
||||||
|
return Math.max(time, currentTimeMillis() + 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingIntent getPendingIntent(String tag) {
|
||||||
|
switch (tag) {
|
||||||
|
case ReminderJob.TAG:
|
||||||
|
return getPendingBroadcast(ReminderJobBroadcast.class);
|
||||||
|
case AlarmJob.TAG:
|
||||||
|
return getPendingBroadcast(AlarmJobBroadcast.class);
|
||||||
|
case RefreshJob.TAG:
|
||||||
|
return getPendingService(RefreshJob.class);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unexpected tag: " + tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> PendingIntent getPendingBroadcast(Class<T> c) {
|
||||||
|
return PendingIntent.getBroadcast(context, 0, new Intent(context, c), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> PendingIntent getPendingService(Class<T> c) {
|
||||||
|
return PendingIntent.getService(context, 0, new Intent(context, c), 0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
|
import com.google.common.collect.Ordering;
|
||||||
|
import com.google.common.collect.TreeMultimap;
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Iterables.filter;
|
||||||
|
import static com.google.common.collect.Lists.newArrayList;
|
||||||
|
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
||||||
|
|
||||||
|
public class JobQueue<T extends JobQueueEntry> {
|
||||||
|
private final TreeMultimap<Long, T> jobs = TreeMultimap.create(Ordering.natural(), (l, r) -> Longs.compare(l.getId(), r.getId()));
|
||||||
|
private final Preferences preferences;
|
||||||
|
private final JobManager jobManager;
|
||||||
|
private final String tag;
|
||||||
|
|
||||||
|
public static JobQueue<Reminder> newReminderQueue(Preferences preferences, JobManager jobManager) {
|
||||||
|
return new JobQueue<>(preferences, jobManager, ReminderJob.TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JobQueue<Alarm> newAlarmQueue(Preferences preferences, JobManager jobManager) {
|
||||||
|
return new JobQueue<>(preferences, jobManager, AlarmJob.TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
JobQueue(Preferences preferences, JobManager jobManager, String tag) {
|
||||||
|
this.preferences = preferences;
|
||||||
|
this.jobManager = jobManager;
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void add(T entry) {
|
||||||
|
boolean reschedule = jobs.isEmpty() || entry.getTime() < firstTime();
|
||||||
|
jobs.put(entry.getTime(), entry);
|
||||||
|
if (reschedule) {
|
||||||
|
scheduleNext(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void clear() {
|
||||||
|
jobs.clear();
|
||||||
|
jobManager.cancel(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void cancel(long id) {
|
||||||
|
boolean reschedule = false;
|
||||||
|
long firstTime = firstTime();
|
||||||
|
List<T> existing = newArrayList(filter(jobs.values(), r -> r.getId() == id));
|
||||||
|
for (T entry : existing) {
|
||||||
|
reschedule |= entry.getTime() == firstTime;
|
||||||
|
jobs.remove(entry.getTime(), entry);
|
||||||
|
}
|
||||||
|
if (reschedule) {
|
||||||
|
scheduleNext(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<T> getOverdueJobs() {
|
||||||
|
List<T> result = newArrayList();
|
||||||
|
for (Long key : jobs.keySet().headSet(currentTimeMillis() + 1)) {
|
||||||
|
result.addAll(jobs.get(key));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean remove(T entry) {
|
||||||
|
return jobs.remove(entry.getTime(), entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void scheduleNext() {
|
||||||
|
scheduleNext(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleNext(boolean cancelCurrent) {
|
||||||
|
if (jobs.isEmpty()) {
|
||||||
|
if (cancelCurrent) {
|
||||||
|
jobManager.cancel(tag);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
jobManager.schedule(tag, nextScheduledTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long firstTime() {
|
||||||
|
return jobs.isEmpty() ? 0 : jobs.asMap().firstKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
long nextScheduledTime() {
|
||||||
|
long next = firstTime();
|
||||||
|
return next > 0 ? preferences.adjustForQuietHours(next) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return jobs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T> getJobs() {
|
||||||
|
return ImmutableList.copyOf(jobs.values());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
public interface JobQueueEntry {
|
||||||
|
long getId();
|
||||||
|
|
||||||
|
long getTime();
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
public abstract class MidnightJob extends Job {
|
||||||
|
public MidnightJob(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import org.tasks.Broadcaster;
|
||||||
|
import org.tasks.injection.IntentServiceComponent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class MidnightRefreshJob extends MidnightJob {
|
||||||
|
|
||||||
|
public static final String TAG = "job_midnight_refresh";
|
||||||
|
|
||||||
|
@Inject Broadcaster broadcaster;
|
||||||
|
@Inject JobManager jobManager;
|
||||||
|
|
||||||
|
public MidnightRefreshJob() {
|
||||||
|
super(MidnightRefreshJob.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
broadcaster.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduleNext() {
|
||||||
|
jobManager.scheduleMidnightRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(IntentServiceComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import org.tasks.Broadcaster;
|
||||||
|
import org.tasks.injection.IntentServiceComponent;
|
||||||
|
import org.tasks.scheduling.RefreshScheduler;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class RefreshJob extends Job {
|
||||||
|
|
||||||
|
public static final String TAG = "job_refresh";
|
||||||
|
|
||||||
|
@Inject RefreshScheduler refreshScheduler;
|
||||||
|
@Inject Broadcaster broadcaster;
|
||||||
|
|
||||||
|
public RefreshJob() {
|
||||||
|
super(RefreshJob.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(IntentServiceComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
broadcaster.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduleNext() {
|
||||||
|
refreshScheduler.scheduleNext();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
public class Reminder implements JobQueueEntry {
|
||||||
|
private final long taskId;
|
||||||
|
private final long time;
|
||||||
|
private final int type;
|
||||||
|
|
||||||
|
public Reminder(long taskId, long time, int type) {
|
||||||
|
this.taskId = taskId;
|
||||||
|
this.time = time;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Reminder reminder = (Reminder) o;
|
||||||
|
|
||||||
|
if (taskId != reminder.taskId) return false;
|
||||||
|
if (time != reminder.time) return false;
|
||||||
|
return type == reminder.type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = (int) (taskId ^ (taskId >>> 32));
|
||||||
|
result = 31 * result + (int) (time ^ (time >>> 32));
|
||||||
|
result = 31 * result + type;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Reminder{" +
|
||||||
|
"taskId=" + taskId +
|
||||||
|
", time=" + time +
|
||||||
|
", type=" + type +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.todoroo.astrid.reminders.ReminderService;
|
||||||
|
|
||||||
|
import org.tasks.Notifier;
|
||||||
|
import org.tasks.injection.IntentServiceComponent;
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class ReminderJob extends WakefulJob {
|
||||||
|
|
||||||
|
public static final String TAG = "job_reminder";
|
||||||
|
|
||||||
|
@Inject Preferences preferences;
|
||||||
|
@Inject ReminderService reminderService;
|
||||||
|
@Inject Notifier notifier;
|
||||||
|
|
||||||
|
public ReminderJob() {
|
||||||
|
super(ReminderJob.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(IntentServiceComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
if (!preferences.isCurrentlyQuietHours()) {
|
||||||
|
for (Reminder reminder : reminderService.getPastReminders()) {
|
||||||
|
notifier.triggerTaskNotification(reminder.getId(), reminder.getType());
|
||||||
|
reminderService.remove(reminder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduleNext() {
|
||||||
|
reminderService.scheduleNextJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completeWakefulIntent(Intent intent) {
|
||||||
|
ReminderJobBroadcast.completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||||
|
|
||||||
|
public class ReminderJobBroadcast extends WakefulBroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
startWakefulService(context, new Intent(context, ReminderJob.class));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.tasks.jobs;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
public abstract class WakefulJob extends Job {
|
||||||
|
|
||||||
|
public WakefulJob(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
super.onHandleIntent(intent);
|
||||||
|
completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void completeWakefulIntent(Intent intent);
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package org.tasks.receivers;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import org.tasks.Broadcaster;
|
|
||||||
import org.tasks.injection.BroadcastComponent;
|
|
||||||
import org.tasks.injection.InjectingBroadcastReceiver;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class RefreshReceiver extends InjectingBroadcastReceiver {
|
|
||||||
|
|
||||||
@Inject Broadcaster broadcaster;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
super.onReceive(context, intent);
|
|
||||||
|
|
||||||
Timber.d("onReceive(context, %s)", intent);
|
|
||||||
|
|
||||||
broadcaster.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(BroadcastComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package org.tasks.receivers;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import org.tasks.Notifier;
|
|
||||||
import org.tasks.injection.BroadcastComponent;
|
|
||||||
import org.tasks.injection.InjectingBroadcastReceiver;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class TaskNotificationReceiver extends InjectingBroadcastReceiver {
|
|
||||||
|
|
||||||
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
|
||||||
|
|
||||||
public static final String ID_KEY = "id"; //$NON-NLS-1$
|
|
||||||
public static final String EXTRAS_TYPE = "type"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
@Inject Notifier notifier;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, final Intent intent) {
|
|
||||||
super.onReceive(context, intent);
|
|
||||||
|
|
||||||
executorService.execute(() -> notifier.triggerTaskNotification(
|
|
||||||
intent.getLongExtra(ID_KEY, 0),
|
|
||||||
intent.getIntExtra(EXTRAS_TYPE, (byte) 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(BroadcastComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.tasks.reminders;
|
||||||
|
|
||||||
|
public class Random {
|
||||||
|
|
||||||
|
private static final java.util.Random random = new java.util.Random();
|
||||||
|
|
||||||
|
public float nextFloat() {
|
||||||
|
return random.nextFloat();
|
||||||
|
}
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
package org.tasks.scheduling;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import org.tasks.injection.InjectingIntentService;
|
|
||||||
import org.tasks.preferences.Preferences;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
|
||||||
import static org.tasks.time.DateTimeUtils.nextMidnight;
|
|
||||||
import static org.tasks.time.DateTimeUtils.printTimestamp;
|
|
||||||
|
|
||||||
public abstract class MidnightIntentService extends InjectingIntentService {
|
|
||||||
|
|
||||||
private static final long PADDING = SECONDS.toMillis(1);
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
MidnightIntentService(String name) {
|
|
||||||
super(name);
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHandleIntent(Intent intent) {
|
|
||||||
super.onHandleIntent(intent);
|
|
||||||
|
|
||||||
Context context = getApplicationContext();
|
|
||||||
Preferences preferences = new Preferences(context);
|
|
||||||
AlarmManager alarmManager = new AlarmManager(context, preferences);
|
|
||||||
|
|
||||||
long lastRun = preferences.getLong(getLastRunPreference(), 0);
|
|
||||||
long nextRun = nextMidnight(lastRun);
|
|
||||||
long now = currentTimeMillis();
|
|
||||||
|
|
||||||
if (nextRun <= now) {
|
|
||||||
nextRun = nextMidnight(now);
|
|
||||||
Timber.d("%s running now [nextRun=%s]", name, printTimestamp(nextRun));
|
|
||||||
if (!isNullOrEmpty(getLastRunPreference())) {
|
|
||||||
preferences.setLong(getLastRunPreference(), now);
|
|
||||||
}
|
|
||||||
run();
|
|
||||||
} else {
|
|
||||||
Timber.d("%s will run at %s [lastRun=%s]", name, printTimestamp(nextRun), printTimestamp(lastRun));
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getService(this, 0, new Intent(this, this.getClass()), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
alarmManager.noWakeup(nextRun + PADDING, pendingIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract void run();
|
|
||||||
|
|
||||||
String getLastRunPreference() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package org.tasks.scheduling;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.sql.Criterion;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
|
|
||||||
import org.tasks.Broadcaster;
|
|
||||||
import org.tasks.injection.IntentServiceComponent;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
|
||||||
import static org.tasks.time.DateTimeUtils.nextMidnight;
|
|
||||||
|
|
||||||
public class RefreshSchedulerIntentService extends MidnightIntentService {
|
|
||||||
|
|
||||||
@Inject Broadcaster broadcaster;
|
|
||||||
@Inject RefreshScheduler refreshScheduler;
|
|
||||||
@Inject TaskDao taskDao;
|
|
||||||
|
|
||||||
public RefreshSchedulerIntentService() {
|
|
||||||
super(RefreshSchedulerIntentService.class.getSimpleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void run() {
|
|
||||||
scheduleApplicationRefreshes();
|
|
||||||
broadcaster.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scheduleApplicationRefreshes() {
|
|
||||||
long now = currentTimeMillis();
|
|
||||||
long midnight = nextMidnight(now);
|
|
||||||
Criterion criterion = Criterion.or(
|
|
||||||
Criterion.and(Task.HIDE_UNTIL.gt(now), Task.HIDE_UNTIL.lt(midnight)),
|
|
||||||
Criterion.and(Task.DUE_DATE.gt(now), Task.DUE_DATE.lt(midnight)));
|
|
||||||
taskDao.selectActive(criterion, refreshScheduler::scheduleRefresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(IntentServiceComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.tasks.scheduling;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.sql.Criterion;
|
||||||
|
import com.todoroo.astrid.dao.TaskDao;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
|
||||||
|
import org.tasks.injection.InjectingIntentService;
|
||||||
|
import org.tasks.injection.IntentServiceComponent;
|
||||||
|
import org.tasks.jobs.JobManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static java.lang.System.currentTimeMillis;
|
||||||
|
|
||||||
|
public class SchedulerIntentService extends InjectingIntentService {
|
||||||
|
|
||||||
|
@Inject TaskDao taskDao;
|
||||||
|
@Inject JobManager jobManager;
|
||||||
|
@Inject RefreshScheduler refreshScheduler;
|
||||||
|
|
||||||
|
public SchedulerIntentService() {
|
||||||
|
super(SchedulerIntentService.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
super.onHandleIntent(intent);
|
||||||
|
|
||||||
|
Timber.d("onHandleIntent(%s)", intent);
|
||||||
|
|
||||||
|
jobManager.scheduleMidnightBackup();
|
||||||
|
jobManager.scheduleMidnightRefresh();
|
||||||
|
|
||||||
|
refreshScheduler.clear();
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
taskDao.selectActive(
|
||||||
|
Criterion.or(Task.HIDE_UNTIL.gt(now), Task.DUE_DATE.gt(now)),
|
||||||
|
refreshScheduler::scheduleRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(IntentServiceComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue