|
|
|
@ -11,7 +11,7 @@ import android.content.ContentValues;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.Intent;
|
|
|
|
|
|
|
|
|
|
|
|
import com.todoroo.andlib.data.TodorooCursor;
|
|
|
|
import com.todoroo.andlib.data.Callback;
|
|
|
|
import com.todoroo.andlib.sql.Criterion;
|
|
|
|
import com.todoroo.andlib.sql.Criterion;
|
|
|
|
import com.todoroo.andlib.sql.Join;
|
|
|
|
import com.todoroo.andlib.sql.Join;
|
|
|
|
import com.todoroo.andlib.sql.Order;
|
|
|
|
import com.todoroo.andlib.sql.Order;
|
|
|
|
@ -26,14 +26,13 @@ import com.todoroo.astrid.reminders.Notifications;
|
|
|
|
import com.todoroo.astrid.reminders.ReminderService;
|
|
|
|
import com.todoroo.astrid.reminders.ReminderService;
|
|
|
|
import com.todoroo.astrid.service.SynchronizeMetadataCallback;
|
|
|
|
import com.todoroo.astrid.service.SynchronizeMetadataCallback;
|
|
|
|
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
import org.tasks.injection.ForApplication;
|
|
|
|
import org.tasks.injection.ForApplication;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.LinkedHashSet;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import javax.inject.Singleton;
|
|
|
|
import javax.inject.Singleton;
|
|
|
|
@ -47,12 +46,16 @@ import javax.inject.Singleton;
|
|
|
|
@Singleton
|
|
|
|
@Singleton
|
|
|
|
public class AlarmService {
|
|
|
|
public class AlarmService {
|
|
|
|
|
|
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(AlarmService.class);
|
|
|
|
private static final long NO_ALARM = Long.MAX_VALUE;
|
|
|
|
|
|
|
|
|
|
|
|
// --- data retrieval
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private MetadataDao metadataDao;
|
|
|
|
private final MetadataDao metadataDao;
|
|
|
|
private final Context context;
|
|
|
|
private final Context context;
|
|
|
|
|
|
|
|
private final Callback<Metadata> scheduleAlarm = new Callback<Metadata>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void apply(Metadata alarm) {
|
|
|
|
|
|
|
|
scheduleAlarm(alarm);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
@Inject
|
|
|
|
public AlarmService(MetadataDao metadataDao, @ForApplication Context context) {
|
|
|
|
public AlarmService(MetadataDao metadataDao, @ForApplication Context context) {
|
|
|
|
@ -60,11 +63,8 @@ public class AlarmService {
|
|
|
|
this.context = context;
|
|
|
|
this.context = context;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
public void getAlarms(long taskId, Callback<Metadata> callback) {
|
|
|
|
* Return alarms for the given task. PLEASE CLOSE THE CURSOR!
|
|
|
|
metadataDao.query(callback, Query.select(
|
|
|
|
*/
|
|
|
|
|
|
|
|
public TodorooCursor<Metadata> getAlarms(long taskId) {
|
|
|
|
|
|
|
|
return metadataDao.query(Query.select(
|
|
|
|
|
|
|
|
Metadata.PROPERTIES).where(MetadataCriteria.byTaskAndwithKey(
|
|
|
|
Metadata.PROPERTIES).where(MetadataCriteria.byTaskAndwithKey(
|
|
|
|
taskId, AlarmFields.METADATA_KEY)).orderBy(Order.asc(AlarmFields.TIME)));
|
|
|
|
taskId, AlarmFields.METADATA_KEY)).orderBy(Order.asc(AlarmFields.TIME)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -73,8 +73,8 @@ public class AlarmService {
|
|
|
|
* Save the given array of alarms into the database
|
|
|
|
* Save the given array of alarms into the database
|
|
|
|
* @return true if data was changed
|
|
|
|
* @return true if data was changed
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public boolean synchronizeAlarms(final long taskId, LinkedHashSet<Long> alarms) {
|
|
|
|
public boolean synchronizeAlarms(final long taskId, Set<Long> alarms) {
|
|
|
|
ArrayList<Metadata> metadata = new ArrayList<>();
|
|
|
|
List<Metadata> metadata = new ArrayList<>();
|
|
|
|
for(Long alarm : alarms) {
|
|
|
|
for(Long alarm : alarms) {
|
|
|
|
Metadata item = new Metadata();
|
|
|
|
Metadata item = new Metadata();
|
|
|
|
item.setKey(AlarmFields.METADATA_KEY);
|
|
|
|
item.setKey(AlarmFields.METADATA_KEY);
|
|
|
|
@ -85,7 +85,7 @@ public class AlarmService {
|
|
|
|
|
|
|
|
|
|
|
|
final AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
|
|
|
|
final AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
|
|
|
|
|
|
|
|
|
|
|
|
boolean changed = synchronizeMetadata(taskId, metadata, Metadata.KEY.eq(AlarmFields.METADATA_KEY), new SynchronizeMetadataCallback() {
|
|
|
|
boolean changed = synchronizeMetadata(taskId, metadata, new SynchronizeMetadataCallback() {
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public void beforeDeleteMetadata(Metadata m) {
|
|
|
|
public void beforeDeleteMetadata(Metadata m) {
|
|
|
|
// Cancel the alarm before the metadata is deleted
|
|
|
|
// Cancel the alarm before the metadata is deleted
|
|
|
|
@ -102,22 +102,14 @@ public class AlarmService {
|
|
|
|
|
|
|
|
|
|
|
|
// --- alarm scheduling
|
|
|
|
// --- alarm scheduling
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
private void getActiveAlarms(Callback<Metadata> callback) {
|
|
|
|
* Gets a listing of all alarms that are active
|
|
|
|
metadataDao.query(callback, Query.select(Metadata.ID, Metadata.TASK, AlarmFields.TIME).
|
|
|
|
* @return todoroo cursor. PLEASE CLOSE THIS CURSOR!
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private TodorooCursor<Metadata> getActiveAlarms() {
|
|
|
|
|
|
|
|
return metadataDao.query(Query.select(Metadata.ID, Metadata.TASK, AlarmFields.TIME).
|
|
|
|
|
|
|
|
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
|
|
|
|
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
|
|
|
|
where(Criterion.and(TaskCriteria.isActive(), MetadataCriteria.withKey(AlarmFields.METADATA_KEY))));
|
|
|
|
where(Criterion.and(TaskCriteria.isActive(), MetadataCriteria.withKey(AlarmFields.METADATA_KEY))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
private void getActiveAlarmsForTask(long taskId, Callback<Metadata> callback) {
|
|
|
|
* Gets a listing of alarms by task
|
|
|
|
metadataDao.query(callback, Query.select(Metadata.ID, Metadata.TASK, AlarmFields.TIME).
|
|
|
|
* @return todoroo cursor. PLEASE CLOSE THIS CURSOR!
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private TodorooCursor<Metadata> getActiveAlarmsForTask(long taskId) {
|
|
|
|
|
|
|
|
return metadataDao.query(Query.select(Metadata.ID, Metadata.TASK, AlarmFields.TIME).
|
|
|
|
|
|
|
|
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
|
|
|
|
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
|
|
|
|
where(Criterion.and(TaskCriteria.isActive(),
|
|
|
|
where(Criterion.and(TaskCriteria.isActive(),
|
|
|
|
MetadataCriteria.byTaskAndwithKey(taskId, AlarmFields.METADATA_KEY))));
|
|
|
|
MetadataCriteria.byTaskAndwithKey(taskId, AlarmFields.METADATA_KEY))));
|
|
|
|
@ -127,37 +119,14 @@ public class AlarmService {
|
|
|
|
* Schedules all alarms
|
|
|
|
* Schedules all alarms
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public void scheduleAllAlarms() {
|
|
|
|
public void scheduleAllAlarms() {
|
|
|
|
TodorooCursor<Metadata> cursor = getActiveAlarms();
|
|
|
|
getActiveAlarms(scheduleAlarm);
|
|
|
|
try {
|
|
|
|
|
|
|
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
|
|
|
|
|
|
Metadata alarm = new Metadata(cursor);
|
|
|
|
|
|
|
|
scheduleAlarm(alarm);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
// suppress
|
|
|
|
|
|
|
|
log.error(e.getMessage(), e);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
cursor.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static final long NO_ALARM = Long.MAX_VALUE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Schedules alarms for a single task
|
|
|
|
* Schedules alarms for a single task
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public void scheduleAlarms(long taskId) {
|
|
|
|
private void scheduleAlarms(long taskId) {
|
|
|
|
TodorooCursor<Metadata> cursor = getActiveAlarmsForTask(taskId);
|
|
|
|
getActiveAlarmsForTask(taskId, scheduleAlarm);
|
|
|
|
try {
|
|
|
|
|
|
|
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
|
|
|
|
|
|
Metadata alarm = new Metadata(cursor);
|
|
|
|
|
|
|
|
scheduleAlarm(alarm);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.error(e.getMessage(), e);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
cursor.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private PendingIntent pendingIntentForAlarm(Metadata alarm, long taskId) {
|
|
|
|
private PendingIntent pendingIntentForAlarm(Metadata alarm, long taskId) {
|
|
|
|
@ -191,10 +160,9 @@ public class AlarmService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private boolean synchronizeMetadata(long taskId, ArrayList<Metadata> metadata,
|
|
|
|
private boolean synchronizeMetadata(long taskId, List<Metadata> metadata, final SynchronizeMetadataCallback callback) {
|
|
|
|
Criterion metadataCriterion, SynchronizeMetadataCallback callback) {
|
|
|
|
final boolean[] dirty = new boolean[1];
|
|
|
|
boolean dirty = false;
|
|
|
|
final Set<ContentValues> newMetadataValues = new HashSet<>();
|
|
|
|
HashSet<ContentValues> newMetadataValues = new HashSet<>();
|
|
|
|
|
|
|
|
for(Metadata metadatum : metadata) {
|
|
|
|
for(Metadata metadatum : metadata) {
|
|
|
|
metadatum.setTask(taskId);
|
|
|
|
metadatum.setTask(taskId);
|
|
|
|
metadatum.clearValue(Metadata.CREATION_DATE);
|
|
|
|
metadatum.clearValue(Metadata.CREATION_DATE);
|
|
|
|
@ -210,12 +178,9 @@ public class AlarmService {
|
|
|
|
newMetadataValues.add(values);
|
|
|
|
newMetadataValues.add(values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).where(Criterion.and(MetadataCriteria.byTask(taskId),
|
|
|
|
metadataDao.byTaskAndKey(taskId, AlarmFields.METADATA_KEY, new Callback<Metadata>() {
|
|
|
|
metadataCriterion)));
|
|
|
|
@Override
|
|
|
|
try {
|
|
|
|
public void apply(Metadata item) {
|
|
|
|
// try to find matches within our metadata list
|
|
|
|
|
|
|
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
|
|
|
|
|
|
Metadata item = new Metadata(cursor);
|
|
|
|
|
|
|
|
long id = item.getId();
|
|
|
|
long id = item.getId();
|
|
|
|
|
|
|
|
|
|
|
|
// clear item id when matching with incoming values
|
|
|
|
// clear item id when matching with incoming values
|
|
|
|
@ -225,20 +190,17 @@ public class AlarmService {
|
|
|
|
|
|
|
|
|
|
|
|
if(newMetadataValues.contains(itemMergedValues)) {
|
|
|
|
if(newMetadataValues.contains(itemMergedValues)) {
|
|
|
|
newMetadataValues.remove(itemMergedValues);
|
|
|
|
newMetadataValues.remove(itemMergedValues);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
// not matched. cut it
|
|
|
|
|
|
|
|
item.setId(id);
|
|
|
|
// not matched. cut it
|
|
|
|
if (callback != null) {
|
|
|
|
item.setId(id);
|
|
|
|
callback.beforeDeleteMetadata(item);
|
|
|
|
if (callback != null) {
|
|
|
|
}
|
|
|
|
callback.beforeDeleteMetadata(item);
|
|
|
|
metadataDao.delete(id);
|
|
|
|
|
|
|
|
dirty[0] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
metadataDao.delete(id);
|
|
|
|
|
|
|
|
dirty = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
});
|
|
|
|
cursor.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// everything that remains shall be written
|
|
|
|
// everything that remains shall be written
|
|
|
|
for(ContentValues values : newMetadataValues) {
|
|
|
|
for(ContentValues values : newMetadataValues) {
|
|
|
|
@ -246,9 +208,9 @@ public class AlarmService {
|
|
|
|
item.setCreationDate(DateUtilities.now());
|
|
|
|
item.setCreationDate(DateUtilities.now());
|
|
|
|
item.mergeWith(values);
|
|
|
|
item.mergeWith(values);
|
|
|
|
metadataDao.persist(item);
|
|
|
|
metadataDao.persist(item);
|
|
|
|
dirty = true;
|
|
|
|
dirty[0] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return dirty;
|
|
|
|
return dirty[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|