/** * Copyright (c) 2012 Todoroo Inc * * See the file "LICENSE" for the full license governing this code. */ package com.todoroo.astrid.gtasks; import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import android.text.TextUtils; import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.DependencyInjectionService; 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.astrid.core.PluginServices; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.StoreObject; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer; import com.todoroo.astrid.subtasks.OrderedMetadataListUpdater.OrderedListIterator; import com.todoroo.astrid.sync.SyncProviderUtilities; import com.todoroo.astrid.utility.SyncMetadataService; /** * Service for working with GTasks metadata * * @author Tim Su <tim@todoroo.com> * */ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskContainer> { @Autowired private GtasksPreferenceService gtasksPreferenceService; public GtasksMetadataService() { super(ContextManager.getContext()); DependencyInjectionService.getInstance().inject(this); } @Override public GtasksTaskContainer createContainerFromLocalTask(Task task, ArrayList<Metadata> metadata) { return new GtasksTaskContainer(task, metadata); } @Override public Criterion getLocalMatchCriteria(GtasksTaskContainer remoteTask) { return GtasksMetadata.ID.eq(remoteTask.gtaskMetadata.getValue(GtasksMetadata.ID)); } @Override public Criterion getMetadataCriteria() { return MetadataCriteria.withKey(getMetadataKey()); } @Override public String getMetadataKey() { return GtasksMetadata.METADATA_KEY; } @Override public SyncProviderUtilities getUtilities() { return gtasksPreferenceService; } @Override public Criterion getMetadataWithRemoteId() { return GtasksMetadata.ID.neq(""); //$NON-NLS-1$ } @Override public synchronized void findLocalMatch(GtasksTaskContainer remoteTask) { if(remoteTask.task.getId() != Task.NO_ID) return; TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES). where(Criterion.and(MetadataCriteria.withKey(getMetadataKey()), getLocalMatchCriteria(remoteTask)))); try { if(cursor.getCount() == 0) return; cursor.moveToFirst(); remoteTask.task.setId(cursor.get(Metadata.TASK)); remoteTask.task.setUuid(taskDao.uuidFromLocalId(remoteTask.task.getId())); remoteTask.gtaskMetadata = new Metadata(cursor); } finally { cursor.close(); } } public long localIdForGtasksId(String gtasksId) { TodorooCursor<Metadata> metadata = metadataDao.query(Query.select(Metadata.TASK).where( Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), GtasksMetadata.ID.eq(gtasksId)))); try { if (metadata.getCount() > 0) { metadata.moveToFirst(); return (new Metadata(metadata).getValue(Metadata.TASK)); } else { return AbstractModel.NO_ID; } } finally { metadata.close(); } } @Override protected TodorooCursor<Task> filterLocallyUpdated(TodorooCursor<Task> tasks, long lastSyncDate) { HashSet<Long> taskIds = new HashSet<Long>(); for(tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) taskIds.add(tasks.get(Task.ID)); TodorooCursor<Metadata> metadata = metadataDao.query(Query.select(Metadata.TASK).where( Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), GtasksMetadata.LAST_SYNC.gt(lastSyncDate)))); for(metadata.moveToFirst(); !metadata.isAfterLast(); metadata.moveToNext()) taskIds.remove(metadata.get(Metadata.TASK)); return taskDao.query(Query.select(Task.ID).where( Task.ID.in(taskIds.toArray(new Long[taskIds.size()])))); } // --- list iterating helpers public void iterateThroughList(StoreObject list, OrderedListIterator iterator) { String listId = list.getValue(GtasksList.REMOTE_ID); iterateThroughList(listId, iterator, 0, false); } @SuppressWarnings("nls") public void iterateThroughList(String listId, 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( MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), GtasksMetadata.LIST_ID.eq(listId), startAtCriterion)). orderBy(order); TodorooCursor<Metadata> cursor = PluginServices.getMetadataService().query(query); try { for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { long taskId = cursor.get(Metadata.TASK); Metadata metadata = getTaskMetadata(taskId); if(metadata == null) continue; iterator.processTask(taskId, metadata); } } finally { cursor.close(); } } /** * Gets the remote id string of the parent task * @param gtasksMetadata * @return */ public String getRemoteParentId(Metadata gtasksMetadata) { String parent = null; if (gtasksMetadata.containsNonNullValue(GtasksMetadata.PARENT_TASK)) { long parentId = gtasksMetadata.getValue(GtasksMetadata.PARENT_TASK); Metadata parentMetadata = getTaskMetadata(parentId); 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 * @param listId * @param gtasksMetadata * @return */ public String getRemoteSiblingId(String listId, Metadata gtasksMetadata) { final AtomicInteger indentToMatch = new AtomicInteger(gtasksMetadata.getValue(GtasksMetadata.INDENT).intValue()); final AtomicLong parentToMatch = new AtomicLong(gtasksMetadata.getValue(GtasksMetadata.PARENT_TASK).longValue()); final AtomicReference<String> sibling = new AtomicReference<String>(); OrderedListIterator iterator = new OrderedListIterator() { @Override public void processTask(long taskId, Metadata metadata) { Task t = taskDao.fetch(taskId, Task.TITLE, Task.DELETION_DATE); if (t == null || t.isDeleted()) return; int currIndent = metadata.getValue(GtasksMetadata.INDENT).intValue(); long currParent = metadata.getValue(GtasksMetadata.PARENT_TASK); if (currIndent == indentToMatch.get() && currParent == parentToMatch.get()) { if (sibling.get() == null) { sibling.set(metadata.getValue(GtasksMetadata.ID)); } } } }; this.iterateThroughList(listId, iterator, gtasksMetadata.getValue(GtasksMetadata.ORDER), true); return sibling.get(); } }