Move post-save activity to intent service

pull/574/head
Alex Baker 7 years ago
parent 7dce9854a2
commit 50eedbfe03

@ -56,7 +56,7 @@ public class AdvancedRepeatTest {
task.setDueDate(dayWithTime);
long nextDayWithTime = dayWithTime + DateUtilities.ONE_DAY;
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal(), false);
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), false);
assertDateTimeEquals(nextDayWithTime, nextDueDate);
}
@ -76,7 +76,7 @@ public class AdvancedRepeatTest {
nextDayWithTimeLong += DateUtilities.ONE_DAY;
nextDayWithTimeLong = nextDayWithTimeLong / 1000L * 1000;
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal(), true);
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), true);
assertDateTimeEquals(nextDayWithTimeLong, nextDueDate);
}
@ -224,7 +224,7 @@ public class AdvancedRepeatTest {
// --- helpers
private void computeNextDueDate(boolean fromComplete) throws ParseException{
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal(), fromComplete);
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), fromComplete);
}
private void buildRRule(int interval, Frequency freq, Weekday... weekdays) {

@ -14,7 +14,7 @@ import org.tasks.time.DateTime;
import java.text.ParseException;
import static com.todoroo.astrid.repeats.RepeatTaskCompleteListener.computeNextDueDate;
import static com.todoroo.astrid.repeats.RepeatTaskHelper.computeNextDueDate;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;

@ -19,7 +19,7 @@ import static com.google.ical.values.Frequency.MINUTELY;
import static com.google.ical.values.Frequency.WEEKLY;
import static com.google.ical.values.Frequency.YEARLY;
import static com.todoroo.andlib.utility.DateUtilities.addCalendarMonthsToUnixtime;
import static com.todoroo.astrid.repeats.RepeatTaskCompleteListener.computeNextDueDate;
import static com.todoroo.astrid.repeats.RepeatTaskHelper.computeNextDueDate;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
@ -27,7 +27,7 @@ import static junit.framework.Assert.assertEquals;
@SuppressLint("NewApi")
@RunWith(AndroidJUnit4.class)
public class RepeatTaskCompleteListenerTest {
public class RepeatTaskHelperTest {
private final Task task = new Task();
private final long dueDate;

@ -7,6 +7,7 @@ import com.todoroo.astrid.dao.Database;
import org.tasks.analytics.Tracker;
import org.tasks.db.AppDatabase;
import org.tasks.notifications.NotificationDao;
import org.tasks.preferences.PermissionChecker;
import org.tasks.preferences.PermissivePermissionChecker;
@ -40,6 +41,11 @@ public class TestModule {
return Room.databaseBuilder(context, AppDatabase.class, "test-app-database").build();
}
@Provides
public NotificationDao getNotificationDao(AppDatabase appDatabase) {
return appDatabase.notificationDao();
}
@ApplicationScope
@Provides
@ForApplication

@ -1,50 +0,0 @@
package org.tasks.injection;
import com.todoroo.astrid.alarms.AlarmTaskRepeatListener;
import com.todoroo.astrid.calls.PhoneStateChangedReceiver;
import com.todoroo.astrid.gcal.CalendarAlarmReceiver;
import com.todoroo.astrid.gcal.GCalTaskCompleteListener;
import com.todoroo.astrid.repeats.RepeatTaskCompleteListener;
import com.todoroo.astrid.timers.TimerTaskCompleteListener;
import org.tasks.locale.receiver.FireReceiver;
import org.tasks.notifications.NotificationClearedReceiver;
import org.tasks.receivers.BootCompletedReceiver;
import org.tasks.receivers.CompleteTaskReceiver;
import org.tasks.receivers.MyPackageReplacedReceiver;
import org.tasks.receivers.PushReceiver;
import org.tasks.receivers.TeslaUnreadReceiver;
import org.tasks.widget.TasksWidget;
import dagger.Subcomponent;
@Subcomponent(modules = BroadcastModule.class)
public interface BroadcastComponent {
void inject(FireReceiver fireReceiver);
void inject(TimerTaskCompleteListener timerTaskCompleteListener);
void inject(PhoneStateChangedReceiver phoneStateChangedReceiver);
void inject(AlarmTaskRepeatListener alarmTaskRepeatListener);
void inject(GCalTaskCompleteListener gCalTaskCompleteListener);
void inject(CalendarAlarmReceiver calendarAlarmReceiver);
void inject(RepeatTaskCompleteListener repeatTaskCompleteListener);
void inject(MyPackageReplacedReceiver myPackageReplacedReceiver);
void inject(CompleteTaskReceiver completeTaskReceiver);
void inject(BootCompletedReceiver bootCompletedReceiver);
void inject(TasksWidget tasksWidget);
void inject(TeslaUnreadReceiver teslaUnreadReceiver);
void inject(PushReceiver pushReceiver);
void inject(NotificationClearedReceiver notificationClearedReceiver);
}

@ -57,13 +57,6 @@
android:label="@string/synchronization"
android:theme="@style/Tasks" />
<receiver android:name=".receivers.GoogleTaskPushReceiver">
<intent-filter>
<action android:name="org.tasks.TASK_SAVED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- Google Analytics -->
<receiver

@ -1,53 +0,0 @@
package org.tasks.injection;
import com.todoroo.astrid.alarms.AlarmTaskRepeatListener;
import com.todoroo.astrid.calls.PhoneStateChangedReceiver;
import com.todoroo.astrid.gcal.CalendarAlarmReceiver;
import com.todoroo.astrid.gcal.GCalTaskCompleteListener;
import com.todoroo.astrid.repeats.RepeatTaskCompleteListener;
import com.todoroo.astrid.timers.TimerTaskCompleteListener;
import org.tasks.locale.receiver.FireReceiver;
import org.tasks.notifications.NotificationClearedReceiver;
import org.tasks.receivers.BootCompletedReceiver;
import org.tasks.receivers.CompleteTaskReceiver;
import org.tasks.receivers.GoogleTaskPushReceiver;
import org.tasks.receivers.MyPackageReplacedReceiver;
import org.tasks.receivers.PushReceiver;
import org.tasks.receivers.TeslaUnreadReceiver;
import org.tasks.widget.TasksWidget;
import dagger.Subcomponent;
@Subcomponent(modules = BroadcastModule.class)
public interface BroadcastComponent {
void inject(FireReceiver fireReceiver);
void inject(GoogleTaskPushReceiver forceSyncReceiver);
void inject(TimerTaskCompleteListener timerTaskCompleteListener);
void inject(PhoneStateChangedReceiver phoneStateChangedReceiver);
void inject(AlarmTaskRepeatListener alarmTaskRepeatListener);
void inject(GCalTaskCompleteListener gCalTaskCompleteListener);
void inject(CalendarAlarmReceiver calendarAlarmReceiver);
void inject(RepeatTaskCompleteListener repeatTaskCompleteListener);
void inject(MyPackageReplacedReceiver myPackageReplacedReceiver);
void inject(CompleteTaskReceiver completeTaskReceiver);
void inject(BootCompletedReceiver bootCompletedReceiver);
void inject(TasksWidget tasksWidget);
void inject(TeslaUnreadReceiver teslaUnreadReceiver);
void inject(PushReceiver pushReceiver);
void inject(NotificationClearedReceiver notificationClearedReceiver);
}

@ -1,74 +0,0 @@
package org.tasks.receivers;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.os.Parcelable;
import com.todoroo.andlib.data.Property;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import javax.inject.Inject;
public class GoogleTaskPushReceiver extends InjectingBroadcastReceiver {
public static void broadcast(Context context, Task task, ContentValues values) {
Intent intent = new Intent(context, GoogleTaskPushReceiver.class);
intent.putExtra(AstridApiConstants.EXTRAS_TASK, task);
intent.putExtra(AstridApiConstants.EXTRAS_VALUES, values);
context.sendBroadcast(intent);
}
private static final Property<?>[] TASK_PROPERTIES = { Task.ID, Task.TITLE,
Task.NOTES, Task.DUE_DATE, Task.COMPLETION_DATE, Task.DELETION_DATE };
@Inject SyncAdapterHelper syncAdapterHelper;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if(!syncAdapterHelper.isEnabled()) {
return;
}
Task model = intent.getParcelableExtra(AstridApiConstants.EXTRAS_TASK);
ContentValues setValues = intent.getParcelableExtra(AstridApiConstants.EXTRAS_VALUES);
if (model == null) {
return;
}
if(model.checkTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC)) {
return;
}
if (checkValuesForProperties(setValues, TASK_PROPERTIES) || model.checkTransitory(SyncFlags.FORCE_SYNC)) {
syncAdapterHelper.requestSynchronization();
}
}
@Override
protected void inject(BroadcastComponent component) {
component.inject(this);
}
/**
* Checks to see if any of the values changed are among the properties we sync
* @return false if none of the properties we sync were changed, true otherwise
*/
private boolean checkValuesForProperties(ContentValues values, Property<?>[] properties) {
if (values == null) {
return false;
}
for (Property<?> property : properties) {
if (property != Task.ID && values.containsKey(property.name)) {
return true;
}
}
return false;
}
}

@ -0,0 +1,50 @@
package org.tasks.receivers;
import android.content.ContentValues;
import com.todoroo.andlib.data.Property;
import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task;
import org.tasks.gtasks.SyncAdapterHelper;
import javax.inject.Inject;
public class GoogleTaskPusher {
private static final Property<?>[] GOOGLE_TASK_PROPERTIES = { Task.ID, Task.TITLE,
Task.NOTES, Task.DUE_DATE, Task.COMPLETION_DATE, Task.DELETION_DATE };
private final SyncAdapterHelper syncAdapterHelper;
@Inject
public GoogleTaskPusher(SyncAdapterHelper syncAdapterHelper) {
this.syncAdapterHelper = syncAdapterHelper;
}
void push(Task task, ContentValues modifiedValues) {
if(!syncAdapterHelper.isEnabled()) {
return;
}
if(task.checkTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC)) {
return;
}
if (checkValuesForProperties(modifiedValues, GOOGLE_TASK_PROPERTIES) || task.checkTransitory(SyncFlags.FORCE_SYNC)) {
syncAdapterHelper.requestSynchronization();
}
}
private boolean checkValuesForProperties(ContentValues values, Property<?>[] properties) {
if (values == null) {
return false;
}
for (Property<?> property : properties) {
if (property != Task.ID && values.containsKey(property.name)) {
return true;
}
}
return false;
}
}

@ -10,6 +10,8 @@ import com.todoroo.astrid.data.Task;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import javax.inject.Inject;
public class PushReceiver extends InjectingBroadcastReceiver {
public static void broadcast(Context context, Task task, ContentValues values) {
@ -19,12 +21,13 @@ public class PushReceiver extends InjectingBroadcastReceiver {
context.sendBroadcast(intent);
}
@Inject GoogleTaskPusher googleTaskPusher;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
GoogleTaskPushReceiver.broadcast(
context,
googleTaskPusher.push(
intent.getParcelableExtra(AstridApiConstants.EXTRAS_TASK),
intent.getParcelableExtra(AstridApiConstants.EXTRAS_VALUES));
}

@ -328,22 +328,12 @@
android:name="com.todoroo.astrid.core.CustomFilterActivity"
android:theme="@style/Tasks"/>
<!-- alarms -->
<receiver android:name="com.todoroo.astrid.alarms.AlarmTaskRepeatListener" />
<!-- actfm -->
<activity android:name=".activities.TagSettingsActivity" />
<activity android:name=".activities.FilterSettingsActivity" />
<!-- repeats -->
<receiver android:name="com.todoroo.astrid.repeats.RepeatTaskCompleteListener" />
<!-- calendar -->
<receiver android:name="com.todoroo.astrid.gcal.GCalTaskCompleteListener" />
<activity
android:name=".activities.CalendarSelectionActivity"
android:theme="@style/TasksDialog" />
@ -381,8 +371,6 @@
android:name=".files.FileExplore"
android:theme="@style/TranslucentDialog"/>
<receiver android:name="com.todoroo.astrid.timers.TimerTaskCompleteListener" />
<!-- reminders -->
<activity
android:name="com.todoroo.astrid.reminders.ReminderPreferences"
@ -415,6 +403,10 @@
</intent-filter>
</receiver>
<service
android:name=".jobs.AfterSaveIntentService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".scheduling.GeofenceSchedulingIntentService"
android:permission="android.permission.BIND_JOB_SERVICE"

@ -1,62 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.alarms;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.inject.Inject;
public class AlarmTaskRepeatListener extends InjectingBroadcastReceiver {
public static void broadcast(Context context, long taskId, long oldDueDate, long newDueDate) {
Intent intent = new Intent(context, AlarmTaskRepeatListener.class);
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
intent.putExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, oldDueDate);
intent.putExtra(AstridApiConstants.EXTRAS_NEW_DUE_DATE, newDueDate);
context.sendBroadcast(intent);
}
@Inject AlarmService alarmService;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1) {
return;
}
long oldDueDateExtra = intent.getLongExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, 0);
final long oldDueDate = oldDueDateExtra == 0 ? DateUtilities.now() : oldDueDateExtra;
final long newDueDate = intent.getLongExtra(AstridApiConstants.EXTRAS_NEW_DUE_DATE, -1);
if(newDueDate <= 0 || newDueDate <= oldDueDate) {
return;
}
final Set<Long> alarms = new LinkedHashSet<>();
alarmService.getAlarms(taskId, metadata -> alarms.add(metadata.getValue(AlarmFields.TIME) + (newDueDate - oldDueDate)));
if (!alarms.isEmpty()) {
alarmService.synchronizeAlarms(taskId, alarms);
}
}
@Override
protected void inject(BroadcastComponent component) {
component.inject(this);
}
}

@ -24,20 +24,14 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskApiDao;
import com.todoroo.astrid.gcal.GCalTaskCompleteListener;
import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.repeats.RepeatTaskCompleteListener;
import com.todoroo.astrid.timers.TimerTaskCompleteListener;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
import org.tasks.location.GeofenceService;
import org.tasks.notifications.NotificationManager;
import org.tasks.jobs.AfterSaveIntentService;
import org.tasks.preferences.Preferences;
import org.tasks.receivers.PushReceiver;
import org.tasks.scheduling.RefreshScheduler;
import java.util.List;
@ -57,30 +51,20 @@ public class TaskDao {
public static final String TRANS_SUPPRESS_REFRESH = "suppress-refresh";
private final RemoteModelDao<Task> dao;
private final RefreshScheduler refreshScheduler;
private final LocalBroadcastManager localBroadcastManager;
private final MetadataDao metadataDao;
private final ReminderService reminderService;
private final NotificationManager notificationManager;
private final Preferences preferences;
private Context context;
private final GeofenceService geofenceService;
private final Context context;
@Inject
public TaskDao(@ForApplication Context context, Database database, MetadataDao metadataDao,
ReminderService reminderService, NotificationManager notificationManager,
Preferences preferences, GeofenceService geofenceService,
RefreshScheduler refreshScheduler, LocalBroadcastManager localBroadcastManager) {
Preferences preferences, LocalBroadcastManager localBroadcastManager) {
this.context = context;
this.geofenceService = geofenceService;
this.preferences = preferences;
this.metadataDao = metadataDao;
this.reminderService = reminderService;
this.notificationManager = notificationManager;
dao = new RemoteModelDao<>(database, Task.class);
this.refreshScheduler = refreshScheduler;
this.localBroadcastManager = localBroadcastManager;
dao = new RemoteModelDao<>(database, Task.class);
}
public TodorooCursor<Task> query(Query query) {
@ -234,7 +218,7 @@ public class TaskDao {
public void save(Task task) {
ContentValues modifiedValues = createOrUpdate(task);
if (modifiedValues != null) {
afterSave(task, modifiedValues);
AfterSaveIntentService.enqueue(context, task.getId(), modifiedValues);
} else if (task.checkTransitory(SyncFlags.FORCE_SYNC)) {
PushReceiver.broadcast(context, task, null);
}
@ -399,72 +383,6 @@ public class TaskDao {
}
}
/**
* Called after the task is saved. This differs from the call in
* TaskApiDao in that it runs hooks that need to be run from within
* Astrid. Order matters here!
*/
private void afterSave(Task task, ContentValues values) {
task.markSaved();
boolean completionDateModified = values.containsKey(Task.COMPLETION_DATE.name);
boolean deletionDateModified = values.containsKey(Task.DELETION_DATE.name);
if(completionDateModified && task.isCompleted()) {
afterComplete(task);
} else if (deletionDateModified && task.isDeleted()) {
afterComplete(task);
} else {
if (completionDateModified || deletionDateModified) {
geofenceService.setupGeofences(task.getId());
}
if(values.containsKey(Task.DUE_DATE.name) ||
values.containsKey(Task.REMINDER_FLAGS.name) ||
values.containsKey(Task.REMINDER_PERIOD.name) ||
values.containsKey(Task.REMINDER_LAST.name) ||
values.containsKey(Task.REMINDER_SNOOZE.name)) {
reminderService.scheduleAlarm(this, task);
}
}
// run api save hooks
broadcastTaskSave(task, values);
}
/**
* Send broadcasts on task change (triggers things like task repeats)
* @param task task that was saved
* @param values values that were updated
*/
private void broadcastTaskSave(Task task, ContentValues values) {
if(TaskApiDao.insignificantChange(values)) {
return;
}
if(values.containsKey(Task.COMPLETION_DATE.name) && task.isCompleted()) {
RepeatTaskCompleteListener.broadcast(context, task.getId());
GCalTaskCompleteListener.broadcast(context, task.getId());
TimerTaskCompleteListener.broadcast(context, task.getId());
}
PushReceiver.broadcast(context, task, values);
refreshScheduler.scheduleRefresh(task);
broadcastRefresh(task);
}
private void broadcastRefresh(Task task) {
if (!task.checkAndClearTransitory(TRANS_SUPPRESS_REFRESH)) {
localBroadcastManager.broadcastRefresh();
}
}
/**
* Called after the task was just completed
*/
private void afterComplete(Task task) {
long taskId = task.getId();
notificationManager.cancel(taskId);
geofenceService.cancelGeofences(taskId);
}
/**
* Mark the given task as completed and save it.
*/
@ -482,7 +400,7 @@ public class TaskDao {
return query(fetchFilteredQuery(queryTemplate, properties));
}
public Query fetchFilteredQuery(String queryTemplate, Property<?>... properties) {
private Query fetchFilteredQuery(String queryTemplate, Property<?>... properties) {
if (queryTemplate == null) {
return Query.selectDistinct(properties);
}

@ -1,72 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gcal;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.CalendarContract;
import android.text.TextUtils;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import javax.inject.Inject;
import timber.log.Timber;
public class GCalTaskCompleteListener extends InjectingBroadcastReceiver {
public static void broadcast(Context context, long taskId) {
Intent intent = new Intent(context, GCalTaskCompleteListener.class);
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(intent);
}
@Inject TaskDao taskDao;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1) {
return;
}
Task task = taskDao.fetch(taskId, Task.ID, Task.TITLE, Task.CALENDAR_URI);
if(task == null) {
return;
}
String calendarUri = task.getCalendarURI();
if(!TextUtils.isEmpty(calendarUri)) {
try {
// change title of calendar event
ContentResolver cr = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.TITLE, context.getString(R.string.gcal_completed_title,
task.getTitle()));
cr.update(Uri.parse(calendarUri), values, null, null);
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
}
}
@Override
protected void inject(BroadcastComponent component) {
component.inject(this);
}
}

@ -5,9 +5,6 @@
*/
package com.todoroo.astrid.repeats;
import android.content.Context;
import android.content.Intent;
import com.google.ical.iter.RecurrenceIterator;
import com.google.ical.iter.RecurrenceIteratorFactory;
import com.google.ical.values.DateTimeValueImpl;
@ -17,21 +14,21 @@ import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.google.ical.values.WeekdayNum;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.alarms.AlarmTaskRepeatListener;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.alarms.AlarmFields;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.GCalHelper;
import org.tasks.LocalBroadcastManager;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import org.tasks.time.DateTime;
import java.text.ParseException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import javax.inject.Inject;
@ -42,32 +39,23 @@ import static org.tasks.date.DateTimeUtils.newDate;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.date.DateTimeUtils.newDateUtc;
public class RepeatTaskCompleteListener extends InjectingBroadcastReceiver {
public static void broadcast(Context context, long taskId) {
Intent intent = new Intent(context, RepeatTaskCompleteListener.class);
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(intent);
}
@Inject GCalHelper gcalHelper;
@Inject TaskDao taskDao;
@Inject LocalBroadcastManager localBroadcastManager;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
public class RepeatTaskHelper {
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1) {
return;
}
private final GCalHelper gcalHelper;
private final TaskDao taskDao;
private final LocalBroadcastManager localBroadcastManager;
private final AlarmService alarmService;
Task task = taskDao.fetch(taskId, Task.PROPERTIES);
if(task == null || !task.isCompleted()) {
return;
}
@Inject
public RepeatTaskHelper(GCalHelper gcalHelper, AlarmService alarmService,
TaskDao taskDao, LocalBroadcastManager localBroadcastManager) {
this.gcalHelper = gcalHelper;
this.taskDao = taskDao;
this.localBroadcastManager = localBroadcastManager;
this.alarmService = alarmService;
}
public void handleRepeat(Task task) {
String recurrence = task.sanitizedRecurrence();
boolean repeatAfterCompletion = task.repeatAfterCompletion();
@ -90,24 +78,18 @@ public class RepeatTaskCompleteListener extends InjectingBroadcastReceiver {
return;
}
rescheduleTask(gcalHelper, taskDao, task, newDueDate);
AlarmTaskRepeatListener.broadcast(context, taskId, oldDueDate, newDueDate);
rescheduleTask(task, newDueDate);
rescheduleAlarms(task.getId(), oldDueDate, newDueDate);
localBroadcastManager.broadcastRepeat(task.getId(), oldDueDate, newDueDate);
}
}
@Override
protected void inject(BroadcastComponent component) {
component.inject(this);
}
private static boolean repeatFinished(long newDueDate, long repeatUntil) {
return repeatUntil > 0 && newDateTime(newDueDate).startOfDay().isAfter(newDateTime(repeatUntil).startOfDay());
}
private static void rescheduleTask(GCalHelper gcalHelper, TaskDao taskDao, Task task, long newDueDate) {
private void rescheduleTask(Task task, long newDueDate) {
task.setReminderSnooze(0L);
task.setCompletionDate(0L);
task.setDueDateAdjustingHideUntil(newDueDate);
@ -116,8 +98,20 @@ public class RepeatTaskCompleteListener extends InjectingBroadcastReceiver {
taskDao.save(task);
}
private void rescheduleAlarms(long taskId, long oldDueDate, long newDueDate) {
if(newDueDate <= 0 || newDueDate <= oldDueDate) {
return;
}
final Set<Long> alarms = new LinkedHashSet<>();
alarmService.getAlarms(taskId, metadata -> alarms.add(metadata.getValue(AlarmFields.TIME) + (newDueDate - oldDueDate)));
if (!alarms.isEmpty()) {
alarmService.synchronizeAlarms(taskId, alarms);
}
}
/** Compute next due date */
public static long computeNextDueDate(Task task, String recurrence, boolean repeatAfterCompletion) throws ParseException {
static long computeNextDueDate(Task task, String recurrence, boolean repeatAfterCompletion) throws ParseException {
RRule rrule = initRRule(recurrence);
// initialize startDateAsDV

@ -1,53 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.timers;
import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import javax.inject.Inject;
public class TimerTaskCompleteListener extends InjectingBroadcastReceiver {
public static void broadcast(Context context, long taskId) {
Intent intent = new Intent(context, TimerTaskCompleteListener.class);
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(intent);
}
@Inject TaskDao taskDao;
@Inject TimerPlugin timerPlugin;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1) {
return;
}
Task task = taskDao.fetch(taskId, Task.ID, Task.ELAPSED_SECONDS,
Task.TIMER_START);
if(task == null || task.getTimerStart() == 0) {
return;
}
timerPlugin.stopTimer(task);
}
@Override
protected void inject(BroadcastComponent component) {
component.inject(this);
}
}

@ -1,11 +1,7 @@
package org.tasks.injection;
import com.todoroo.astrid.alarms.AlarmTaskRepeatListener;
import com.todoroo.astrid.calls.PhoneStateChangedReceiver;
import com.todoroo.astrid.gcal.CalendarAlarmReceiver;
import com.todoroo.astrid.gcal.GCalTaskCompleteListener;
import com.todoroo.astrid.repeats.RepeatTaskCompleteListener;
import com.todoroo.astrid.timers.TimerTaskCompleteListener;
import org.tasks.locale.receiver.FireReceiver;
import org.tasks.notifications.NotificationClearedReceiver;
@ -22,18 +18,10 @@ import dagger.Subcomponent;
public interface BroadcastComponent {
void inject(FireReceiver fireReceiver);
void inject(TimerTaskCompleteListener timerTaskCompleteListener);
void inject(PhoneStateChangedReceiver phoneStateChangedReceiver);
void inject(AlarmTaskRepeatListener alarmTaskRepeatListener);
void inject(GCalTaskCompleteListener gCalTaskCompleteListener);
void inject(CalendarAlarmReceiver calendarAlarmReceiver);
void inject(RepeatTaskCompleteListener repeatTaskCompleteListener);
void inject(MyPackageReplacedReceiver myPackageReplacedReceiver);
void inject(CompleteTaskReceiver completeTaskReceiver);

@ -4,6 +4,7 @@ import org.tasks.jobs.NotificationJob;
import org.tasks.jobs.BackupJob;
import org.tasks.jobs.MidnightRefreshJob;
import org.tasks.jobs.RefreshJob;
import org.tasks.jobs.AfterSaveIntentService;
import org.tasks.location.GeofenceTransitionsIntentService;
import org.tasks.scheduling.BackgroundScheduler;
import org.tasks.scheduling.CalendarNotificationIntentService;
@ -31,4 +32,6 @@ public interface IntentServiceComponent {
void inject(RefreshJob refreshJob);
void inject(BackgroundScheduler backgroundScheduler);
void inject(AfterSaveIntentService afterSaveIntentService);
}

@ -0,0 +1,135 @@
package org.tasks.jobs;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.CalendarContract;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskApiDao;
import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.repeats.RepeatTaskHelper;
import com.todoroo.astrid.timers.TimerPlugin;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.injection.InjectingJobIntentService;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.location.GeofenceService;
import org.tasks.notifications.NotificationManager;
import org.tasks.receivers.PushReceiver;
import org.tasks.scheduling.RefreshScheduler;
import javax.inject.Inject;
import timber.log.Timber;
import static com.todoroo.astrid.dao.TaskDao.TRANS_SUPPRESS_REFRESH;
public class AfterSaveIntentService extends InjectingJobIntentService {
private static final String EXTRA_TASK_ID = "extra_task_id";
private static final String EXTRA_MODIFIED_VALUES = "extra_modified_values";
public static void enqueue(Context context, long taskId, ContentValues modifiedValues) {
Intent intent = new Intent();
intent.putExtra(EXTRA_TASK_ID, taskId);
intent.putExtra(EXTRA_MODIFIED_VALUES, modifiedValues);
AfterSaveIntentService.enqueueWork(context, AfterSaveIntentService.class, JobManager.JOB_ID_TASK_STATUS_CHANGE, intent);
}
@Inject RepeatTaskHelper repeatTaskHelper;
@Inject @ForApplication Context context;
@Inject TaskDao taskDao;
@Inject NotificationManager notificationManager;
@Inject GeofenceService geofenceService;
@Inject TimerPlugin timerPlugin;
@Inject ReminderService reminderService;
@Inject RefreshScheduler refreshScheduler;
@Inject LocalBroadcastManager localBroadcastManager;
@Override
protected void onHandleWork(@NonNull Intent intent) {
super.onHandleWork(intent);
long taskId = intent.getLongExtra(EXTRA_TASK_ID, -1);
ContentValues modifiedValues = intent.getParcelableExtra(EXTRA_MODIFIED_VALUES);
if (taskId == -1 || modifiedValues == null) {
Timber.e("Invalid extras, taskId=%s modifiedValues=%s", taskId, modifiedValues);
return;
}
Task task = taskDao.fetch(taskId);
if (task == null) {
Timber.e("Can't find task with id %s", taskId);
return;
}
if(modifiedValues.containsKey(Task.DUE_DATE.name) ||
modifiedValues.containsKey(Task.REMINDER_FLAGS.name) ||
modifiedValues.containsKey(Task.REMINDER_PERIOD.name) ||
modifiedValues.containsKey(Task.REMINDER_LAST.name) ||
modifiedValues.containsKey(Task.REMINDER_SNOOZE.name)) {
reminderService.scheduleAlarm(taskDao, task);
}
if(TaskApiDao.insignificantChange(modifiedValues)) {
return;
}
boolean completionDateModified = modifiedValues.containsKey(Task.COMPLETION_DATE.name);
boolean deletionDateModified = modifiedValues.containsKey(Task.DELETION_DATE.name);
boolean justCompleted = completionDateModified && task.isCompleted();
boolean justDeleted = deletionDateModified && task.isDeleted();
if (justCompleted || justDeleted) {
notificationManager.cancel(taskId);
geofenceService.cancelGeofences(taskId);
} else if (completionDateModified || deletionDateModified) {
geofenceService.setupGeofences(taskId);
}
if (justCompleted) {
repeatTaskHelper.handleRepeat(task);
updateCalendarTitle(task);
if (task.getTimerStart() > 0) {
timerPlugin.stopTimer(task);
}
}
PushReceiver.broadcast(context, task, modifiedValues);
refreshScheduler.scheduleRefresh(task);
if (!task.checkAndClearTransitory(TRANS_SUPPRESS_REFRESH)) {
localBroadcastManager.broadcastRefresh();
}
}
private void updateCalendarTitle(Task task) {
String calendarUri = task.getCalendarURI();
if(!TextUtils.isEmpty(calendarUri)) {
try {
// change title of calendar event
ContentResolver cr = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.TITLE, context.getString(R.string.gcal_completed_title,
task.getTitle()));
cr.update(Uri.parse(calendarUri), values, null, null);
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
}
}
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
}

@ -26,6 +26,7 @@ public class JobManager {
public static final int JOB_ID_GEOFENCE_SCHEDULING = 5;
static final int JOB_ID_MIDNIGHT_REFRESH = 6;
static final int JOB_ID_BACKUP = 7;
public static final int JOB_ID_TASK_STATUS_CHANGE = 8;
public static final int JOB_ID_NOTIFICATION_SCHEDULER = 9;
public static final int JOB_ID_CALENDAR_NOTIFICATION = 10;

Loading…
Cancel
Save