mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
8.4 KiB
Java
229 lines
8.4 KiB
Java
/**
|
|
* Copyright (c) 2012 Todoroo Inc
|
|
*
|
|
* See the file "LICENSE" for the full license governing this code.
|
|
*/
|
|
package com.todoroo.astrid.gtasks.sync;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import com.todoroo.andlib.sql.Criterion;
|
|
import com.todoroo.andlib.sql.Field;
|
|
import com.todoroo.andlib.sql.Functions;
|
|
import com.todoroo.andlib.sql.Order;
|
|
import com.todoroo.andlib.sql.Query;
|
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
import com.todoroo.astrid.dao.MetadataDao;
|
|
import com.todoroo.astrid.dao.TaskDao;
|
|
import com.todoroo.astrid.data.Metadata;
|
|
import com.todoroo.astrid.data.SyncFlags;
|
|
import com.todoroo.astrid.data.Task;
|
|
import com.todoroo.astrid.gtasks.GtasksMetadata;
|
|
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
|
|
import com.todoroo.astrid.gtasks.OrderedMetadataListUpdater;
|
|
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
|
|
import com.todoroo.astrid.gtasks.api.MoveRequest;
|
|
|
|
import org.tasks.gtasks.SyncAdapterHelper;
|
|
import org.tasks.injection.ApplicationScope;
|
|
|
|
import java.io.IOException;
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import timber.log.Timber;
|
|
|
|
@ApplicationScope
|
|
public class GtasksSyncService {
|
|
|
|
private final MetadataDao metadataDao;
|
|
private final TaskDao taskDao;
|
|
private final GtasksPreferenceService gtasksPreferenceService;
|
|
private final GtasksInvoker gtasksInvoker;
|
|
private final LinkedBlockingQueue<SyncOnSaveOperation> operationQueue = new LinkedBlockingQueue<>();
|
|
private final SyncAdapterHelper syncAdapterHelper;
|
|
|
|
@Inject
|
|
public GtasksSyncService(MetadataDao metadataDao, TaskDao taskDao,
|
|
GtasksPreferenceService gtasksPreferenceService,
|
|
GtasksInvoker gtasksInvoker,
|
|
SyncAdapterHelper syncAdapterHelper) {
|
|
this.metadataDao = metadataDao;
|
|
this.taskDao = taskDao;
|
|
this.gtasksPreferenceService = gtasksPreferenceService;
|
|
this.gtasksInvoker = gtasksInvoker;
|
|
this.syncAdapterHelper = syncAdapterHelper;
|
|
new OperationPushThread(operationQueue).start();
|
|
}
|
|
|
|
public interface SyncOnSaveOperation {
|
|
void op(GtasksInvoker invoker) throws IOException;
|
|
}
|
|
|
|
private class MoveOp implements SyncOnSaveOperation {
|
|
protected Metadata metadata;
|
|
|
|
public MoveOp(Metadata metadata) {
|
|
this.metadata = metadata;
|
|
}
|
|
|
|
@Override
|
|
public void op(GtasksInvoker invoker) throws IOException {
|
|
pushMetadataOnSave(metadata, invoker);
|
|
}
|
|
}
|
|
|
|
private class ClearOp implements SyncOnSaveOperation {
|
|
private final String listId;
|
|
|
|
public ClearOp(String listId) {
|
|
this.listId = listId;
|
|
}
|
|
|
|
@Override
|
|
public void op(GtasksInvoker invoker) throws IOException {
|
|
invoker.clearCompleted(listId);
|
|
}
|
|
}
|
|
|
|
private class OperationPushThread extends Thread {
|
|
private final LinkedBlockingQueue<SyncOnSaveOperation> queue;
|
|
|
|
public OperationPushThread(LinkedBlockingQueue<SyncOnSaveOperation> queue) {
|
|
this.queue = queue;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
//noinspection InfiniteLoopStatement
|
|
while (true) {
|
|
SyncOnSaveOperation op;
|
|
try {
|
|
op = queue.take();
|
|
} catch (InterruptedException e) {
|
|
Timber.e(e, e.getMessage());
|
|
continue;
|
|
}
|
|
try {
|
|
op.op(gtasksInvoker);
|
|
} catch (IOException e) {
|
|
Timber.e(e, e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void clearCompleted(String listId) {
|
|
operationQueue.offer(new ClearOp(listId));
|
|
}
|
|
|
|
public void triggerMoveForMetadata(final Metadata metadata) {
|
|
if (metadata == null) {
|
|
return;
|
|
}
|
|
if (metadata.checkAndClearTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC)) {
|
|
return;
|
|
}
|
|
if (!metadata.getKey().equals(GtasksMetadata.METADATA_KEY)) //Don't care about non-gtasks metadata
|
|
{
|
|
return;
|
|
}
|
|
if (gtasksPreferenceService.isOngoing()) //Don't try and sync changes that occur during a normal sync
|
|
{
|
|
return;
|
|
}
|
|
if (!syncAdapterHelper.isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
operationQueue.offer(new MoveOp(metadata));
|
|
}
|
|
|
|
public void pushMetadataOnSave(Metadata model, GtasksInvoker invoker) throws IOException {
|
|
AndroidUtilities.sleepDeep(1000L);
|
|
|
|
String taskId = model.getValue(GtasksMetadata.ID);
|
|
String listId = model.getValue(GtasksMetadata.LIST_ID);
|
|
String parent = getRemoteParentId(model);
|
|
String priorSibling = getRemoteSiblingId(listId, model);
|
|
|
|
MoveRequest move = new MoveRequest(invoker, taskId, listId, parent, priorSibling);
|
|
com.google.api.services.tasks.model.Task result = move.push();
|
|
// Update order metadata from result
|
|
if (result != null) {
|
|
model.setValue(GtasksMetadata.GTASKS_ORDER, Long.parseLong(result.getPosition()));
|
|
model.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true);
|
|
metadataDao.saveExisting(model);
|
|
}
|
|
}
|
|
|
|
public void iterateThroughList(String listId, final OrderedMetadataListUpdater.OrderedListIterator iterator, long startAtOrder, boolean reverse) {
|
|
Field orderField = Functions.cast(GtasksMetadata.ORDER, "LONG");
|
|
Order order = reverse ? Order.desc(orderField) : Order.asc(orderField);
|
|
Criterion startAtCriterion = reverse ? Functions.cast(GtasksMetadata.ORDER, "LONG").lt(startAtOrder) :
|
|
Functions.cast(GtasksMetadata.ORDER, "LONG").gt(startAtOrder - 1);
|
|
|
|
Query query = Query.select(Metadata.PROPERTIES).where(Criterion.and(
|
|
MetadataDao.MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY),
|
|
GtasksMetadata.LIST_ID.eq(listId),
|
|
startAtCriterion)).
|
|
orderBy(order);
|
|
|
|
metadataDao.query(query, entry -> {
|
|
long taskId = entry.getValue(Metadata.TASK);
|
|
Metadata metadata = metadataDao.getFirstActiveByTaskAndKey(taskId, GtasksMetadata.METADATA_KEY);
|
|
if(metadata != null) {
|
|
iterator.processTask(taskId, metadata);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets the remote id string of the parent task
|
|
*/
|
|
public String getRemoteParentId(Metadata gtasksMetadata) {
|
|
String parent = null;
|
|
if (gtasksMetadata.containsNonNullValue(GtasksMetadata.PARENT_TASK)) {
|
|
long parentId = gtasksMetadata.getValue(GtasksMetadata.PARENT_TASK);
|
|
Metadata parentMetadata = metadataDao.getFirstActiveByTaskAndKey(parentId, GtasksMetadata.METADATA_KEY);
|
|
if (parentMetadata != null && parentMetadata.containsNonNullValue(GtasksMetadata.ID)) {
|
|
parent = parentMetadata.getValue(GtasksMetadata.ID);
|
|
if (TextUtils.isEmpty(parent)) {
|
|
parent = null;
|
|
}
|
|
}
|
|
}
|
|
return parent;
|
|
}
|
|
|
|
/**
|
|
* Gets the remote id string of the previous sibling task
|
|
*/
|
|
public String getRemoteSiblingId(String listId, Metadata gtasksMetadata) {
|
|
final AtomicInteger indentToMatch = new AtomicInteger(gtasksMetadata.getValue(GtasksMetadata.INDENT));
|
|
final AtomicLong parentToMatch = new AtomicLong(gtasksMetadata.getValue(GtasksMetadata.PARENT_TASK));
|
|
final AtomicReference<String> sibling = new AtomicReference<>();
|
|
OrderedMetadataListUpdater.OrderedListIterator iterator = (taskId, metadata) -> {
|
|
Task t = taskDao.fetch(taskId, Task.TITLE, Task.DELETION_DATE);
|
|
if (t == null || t.isDeleted()) {
|
|
return;
|
|
}
|
|
int currIndent = metadata.getValue(GtasksMetadata.INDENT);
|
|
long currParent = metadata.getValue(GtasksMetadata.PARENT_TASK);
|
|
|
|
if (currIndent == indentToMatch.get() && currParent == parentToMatch.get()) {
|
|
if (sibling.get() == null) {
|
|
sibling.set(metadata.getValue(GtasksMetadata.ID));
|
|
}
|
|
}
|
|
};
|
|
|
|
iterateThroughList(listId, iterator, gtasksMetadata.getValue(GtasksMetadata.ORDER), true);
|
|
return sibling.get();
|
|
}
|
|
}
|