diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksIndentAction.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksIndentAction.java index 6c4a8a366..55b0bb709 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksIndentAction.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksIndentAction.java @@ -3,14 +3,11 @@ package com.todoroo.astrid.gtasks; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.widget.Toast; -import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.astrid.api.AstridApiConstants; -import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.utility.Flags; @@ -40,15 +37,14 @@ abstract public class GtasksIndentAction extends BroadcastReceiver { metadata = GtasksMetadata.createEmptyMetadata(taskId); } - int newIndent = Math.max(0, metadata.getValue(GtasksMetadata.INDENT) + getDelta()); - metadata.setValue(GtasksMetadata.INDENT, newIndent); - PluginServices.getMetadataService().save(metadata); + if(metadata.getValue(GtasksMetadata.INDENT) + getDelta() < 0) + return; - gtasksTaskListUpdater.updateMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID)); + String listId = metadata.getValue(GtasksMetadata.LIST_ID); + gtasksTaskListUpdater.indent(listId, taskId, getDelta()); + gtasksTaskListUpdater.debugPrint(listId); Flags.set(Flags.REFRESH); - Toast.makeText(context, context.getString(R.string.gtasks_indent_toast, newIndent), - Toast.LENGTH_SHORT).show(); } public static class GtasksIncreaseIndentAction extends GtasksIndentAction { diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksOrderAction.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksOrderAction.java index 115f961da..0129f23eb 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksOrderAction.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksOrderAction.java @@ -39,7 +39,7 @@ abstract public class GtasksOrderAction extends BroadcastReceiver { if(metadata == null) return; - gtasksTaskListUpdater.updateMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID)); + gtasksTaskListUpdater.correctMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID)); metadata = gtasksMetadataService.getTaskMetadata(taskId); int oldOrder = metadata.getValue(GtasksMetadata.ORDER); diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksTaskListUpdater.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksTaskListUpdater.java index 05cc1d64d..168d024ed 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksTaskListUpdater.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksTaskListUpdater.java @@ -1,8 +1,6 @@ package com.todoroo.astrid.gtasks; -import java.util.ArrayList; import java.util.HashMap; -import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -25,6 +23,7 @@ public class GtasksTaskListUpdater { final HashMap parents = new HashMap(); final HashMap siblings = new HashMap(); + final HashMap localToRemoteIdMap = new HashMap(); @@ -32,54 +31,113 @@ public class GtasksTaskListUpdater { DependencyInjectionService.getInstance().inject(this); } + // --- used during normal ui operations + + public void debugPrint(String listId) { + StoreObject list = gtasksListService.getList(listId); + if(list == GtasksListService.LIST_NOT_FOUND_OBJECT) + return; + + iterateThroughList(list, new ListIterator() { + public void processTask(long taskId, Metadata metadata) { + System.err.format("%d: indent:%d, parent:%d\n", taskId, //$NON-NLS-1$ + metadata.getValue(GtasksMetadata.INDENT), + metadata.getValue(GtasksMetadata.PARENT_TASK)); + } + }); + } + + /** + * Indent a task and all its children + */ + public void indent(String listId, final long targetTaskId, final int delta) { + StoreObject list = gtasksListService.getList(listId); + if(list == GtasksListService.LIST_NOT_FOUND_OBJECT) + return; + + updateParentSiblingMapsFor(list); + + final AtomicInteger targetTaskIndent = new AtomicInteger(-1); + final AtomicInteger previousIndent = new AtomicInteger(-1); + final AtomicLong previousTask = new AtomicLong(-1); + + iterateThroughList(list, new ListIterator() { + @Override + public void processTask(long taskId, Metadata metadata) { + int indent = metadata.getValue(GtasksMetadata.INDENT); + + if(targetTaskId == taskId) { + // if indenting is warranted, indent me and my children + if(indent + delta <= previousIndent.get() + 1 && indent + delta >= 0) { + targetTaskIndent.set(indent); + metadata.setValue(GtasksMetadata.INDENT, indent + delta); + if(delta > 0) + metadata.setValue(GtasksMetadata.PARENT_TASK, previousTask.get()); + else if(parents.containsKey(taskId)) + metadata.setValue(GtasksMetadata.PARENT_TASK, + parents.get(parents.get(taskId))); + else + metadata.setValue(GtasksMetadata.PARENT_TASK, Task.NO_ID); + PluginServices.getMetadataService().save(metadata); + } + } else if(targetTaskIndent.get() > -1) { + // found first task that is not beneath target + if(indent <= targetTaskIndent.get()) + targetTaskIndent.set(-1); + else { + metadata.setValue(GtasksMetadata.INDENT, indent + delta); + PluginServices.getMetadataService().save(metadata); + } + } else { + previousIndent.set(indent); + previousTask.set(taskId); + } + } + }); + } + + // --- used during synchronization + + + /** + * Update order, parent, and indentation fields for all tasks in all lists + */ + public void updateAllMetadata() { + for(StoreObject list : gtasksListService.getLists()) { + correctMetadataForList(list.getValue(GtasksList.REMOTE_ID)); + } + } + /** - * Update order and parent fields for all tasks in the given list + * Update order, parent, and indentation fields for all tasks in the given list * @param listId */ - public void updateMetadataForList(String listId) { + public void correctMetadataForList(String listId) { StoreObject list = gtasksListService.getList(listId); if(list == GtasksListService.LIST_NOT_FOUND_OBJECT) return; - final ArrayList ids = new ArrayList(); - final Stack taskHierarchyStack = new Stack(); + updateParentSiblingMapsFor(list); final AtomicInteger order = new AtomicInteger(0); final AtomicInteger previousIndent = new AtomicInteger(-1); iterateThroughList(list, new ListIterator() { @Override - public void processTask(long taskId) { - ids.add(taskId); - Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId); - if(metadata == null) - return; - + public void processTask(long taskId, Metadata metadata) { metadata.setValue(GtasksMetadata.ORDER, order.getAndAdd(1)); int indent = metadata.getValue(GtasksMetadata.INDENT); + if(indent > previousIndent.get() + 1) + indent = previousIndent.get() + 1; + metadata.setValue(GtasksMetadata.INDENT, indent); - for(int i = indent; i <= previousIndent.get(); i++) { - if(!taskHierarchyStack.isEmpty()) - taskHierarchyStack.pop(); - } - - if(indent > 0) { - if(taskHierarchyStack.isEmpty()) { - metadata.setValue(GtasksMetadata.PARENT_TASK, 0L); - metadata.setValue(GtasksMetadata.INDENT, 0); - } else - metadata.setValue(GtasksMetadata.PARENT_TASK, taskHierarchyStack.peek()); - } else { - metadata.setValue(GtasksMetadata.PARENT_TASK, 0L); - } + long parent = parents.get(taskId); + metadata.setValue(GtasksMetadata.PARENT_TASK, parent); PluginServices.getMetadataService().save(metadata); - taskHierarchyStack.push(taskId); previousIndent.set(indent); } }); - - PluginServices.getTaskService().clearDetails(Task.ID.in(ids.toArray(new Long[ids.size()]))); } /** @@ -87,44 +145,50 @@ public class GtasksTaskListUpdater { */ public void createParentSiblingMaps() { for(StoreObject list : gtasksListService.getLists()) { - final AtomicLong previousTask = new AtomicLong(-1L); - final AtomicInteger previousIndent = new AtomicInteger(-1); - - iterateThroughList(list, new ListIterator() { - @Override - public void processTask(long taskId) { - Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId); - if(metadata == null) - return; - - int indent = metadata.getValue(GtasksMetadata.INDENT); - - long parent, sibling; - if(indent > previousIndent.get()) { - parent = previousTask.get(); - sibling = -1L; - } else if(indent == previousIndent.get()) { - sibling = previousTask.get(); - parent = parents.get(sibling); - } else { - // move up once for each indent - sibling = previousTask.get(); - for(int i = indent; i < previousIndent.get(); i++) - sibling = parents.get(sibling); - parent = parents.get(sibling); - } - parents.put(taskId, parent); - siblings.put(taskId, sibling); + updateParentSiblingMapsFor(list); + } + } - previousTask.set(taskId); - previousIndent.set(indent); - if(!TextUtils.isEmpty(metadata.getValue(GtasksMetadata.ID))) - localToRemoteIdMap.put(taskId, metadata.getValue(GtasksMetadata.ID)); + private void updateParentSiblingMapsFor(StoreObject list) { + final AtomicLong previousTask = new AtomicLong(-1L); + final AtomicInteger previousIndent = new AtomicInteger(-1); + + iterateThroughList(list, new ListIterator() { + @Override + public void processTask(long taskId, Metadata metadata) { + int indent = metadata.getValue(GtasksMetadata.INDENT); + + long parent, sibling; + if(indent > previousIndent.get()) { + parent = previousTask.get(); + sibling = -1L; + } else if(indent == previousIndent.get()) { + sibling = previousTask.get(); + parent = parents.get(sibling); + } else { + // move up once for each indent + sibling = previousTask.get(); + for(int i = indent; i < previousIndent.get(); i++) + sibling = parents.get(sibling); + parent = parents.get(sibling); } - }); - } + parents.put(taskId, parent); + siblings.put(taskId, sibling); + + previousTask.set(taskId); + previousIndent.set(indent); + if(!TextUtils.isEmpty(metadata.getValue(GtasksMetadata.ID))) + localToRemoteIdMap.put(taskId, metadata.getValue(GtasksMetadata.ID)); + } + }); } + /** + * Must be called after creating parent and sibling maps. Updates a + * task container's parent and sibling fields. + * + * @param container + */ public void updateParentAndSibling(GtasksTaskContainer container) { long taskId = container.task.getId(); if(parents.containsKey(taskId)) { @@ -146,7 +210,7 @@ public class GtasksTaskListUpdater { // --- private helpers private interface ListIterator { - public void processTask(long taskId); + public void processTask(long taskId, Metadata metadata); } private void iterateThroughList(StoreObject list, ListIterator iterator) { @@ -155,12 +219,16 @@ public class GtasksTaskListUpdater { try { for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { long taskId = cursor.getLong(0); - iterator.processTask(taskId); + Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId); + if(metadata == null) + continue; + iterator.processTask(taskId, metadata); } } finally { cursor.close(); } } + } diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java index 6968d3192..fb81f914f 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java @@ -246,6 +246,8 @@ public class GtasksSyncProvider extends SyncProvider { syncData.localUpdated.close(); } + gtasksTaskListUpdater.updateAllMetadata(); + gtasksPreferenceService.recordSuccessfulSync(); FlurryAgent.onEvent("gtasks-sync-finished"); //$NON-NLS-1$ } catch (IllegalStateException e) { diff --git a/tests/src/com/todoroo/astrid/gtasks/GtasksTaskListUpdaterTest.java b/tests/src/com/todoroo/astrid/gtasks/GtasksTaskListUpdaterTest.java index 2bf2c1b06..7df0dfd96 100644 --- a/tests/src/com/todoroo/astrid/gtasks/GtasksTaskListUpdaterTest.java +++ b/tests/src/com/todoroo/astrid/gtasks/GtasksTaskListUpdaterTest.java @@ -125,7 +125,7 @@ public class GtasksTaskListUpdaterTest extends DatabaseTestCase { private void whenCalculatingOrder() { for(StoreObject list : gtasksListService.getLists()) - gtasksTaskListUpdater.updateMetadataForList(list.getValue(GtasksList.REMOTE_ID)); + gtasksTaskListUpdater.correctMetadataForList(list.getValue(GtasksList.REMOTE_ID)); }