diff --git a/src/androidTest/java/com/todoroo/astrid/subtasks/SubtasksTestCase.java b/src/androidTest/java/com/todoroo/astrid/subtasks/SubtasksTestCase.java index 4edb84d30..9c9e054f7 100644 --- a/src/androidTest/java/com/todoroo/astrid/subtasks/SubtasksTestCase.java +++ b/src/androidTest/java/com/todoroo/astrid/subtasks/SubtasksTestCase.java @@ -5,7 +5,7 @@ import com.todoroo.astrid.core.BuiltInFilterExposer; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskListMetadataDao; import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.subtasks.AstridOrderedListUpdater.Node; +import com.todoroo.astrid.subtasks.SubtasksFilterUpdater.Node; import com.todoroo.astrid.test.DatabaseTestCase; import org.tasks.injection.TestComponent; diff --git a/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListFragmentHelper.java b/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListFragmentHelper.java index fc54de746..7f2511ac2 100644 --- a/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListFragmentHelper.java +++ b/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListFragmentHelper.java @@ -45,7 +45,7 @@ import timber.log.Timber; public class AstridOrderedListFragmentHelper { private final DisplayMetrics metrics = new DisplayMetrics(); - private final AstridOrderedListUpdater updater; + private final SubtasksFilterUpdater updater; private final DialogBuilder dialogBuilder; private final CheckBoxes checkBoxes; private final TagService tagService; @@ -60,7 +60,7 @@ public class AstridOrderedListFragmentHelper { private TaskListMetadata list; public AstridOrderedListFragmentHelper(Preferences preferences, TaskAttachmentDao taskAttachmentDao, - TaskListFragment fragment, AstridOrderedListUpdater updater, + TaskListFragment fragment, SubtasksFilterUpdater updater, DialogBuilder dialogBuilder, CheckBoxes checkBoxes, TagService tagService, ThemeCache themeCache, TaskDao taskDao) { this.preferences = preferences; diff --git a/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java b/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java deleted file mode 100644 index 0b074c942..000000000 --- a/src/main/java/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java +++ /dev/null @@ -1,445 +0,0 @@ -package com.todoroo.astrid.subtasks; - -import com.todoroo.andlib.data.TodorooCursor; -import com.todoroo.astrid.api.Filter; -import com.todoroo.astrid.dao.TaskDao; -import com.todoroo.astrid.data.RemoteModel; -import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.data.TaskListMetadata; - -import org.json.JSONArray; -import org.json.JSONException; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import timber.log.Timber; - -public abstract class AstridOrderedListUpdater { - - private final TaskDao taskDao; - - AstridOrderedListUpdater(TaskDao taskDao) { - this.taskDao = taskDao; - idToNode = new HashMap<>(); - } - - public interface OrderedListNodeVisitor { - void visitNode(Node node); - } - - public static class Node { - public String uuid; - public Node parent; - public int indent; - public final ArrayList children = new ArrayList<>(); - - public Node(String uuid, Node parent, int indent) { - this.uuid = uuid; - this.parent = parent; - this.indent = indent; - } - } - - private Node treeRoot; - - private final HashMap idToNode; - - protected abstract String getSerializedTree(TaskListMetadata list); - protected abstract void writeSerialization(TaskListMetadata list, String serialized, boolean shouldQueueSync); - protected abstract void applyToFilter(Filter filter); - - public int getIndentForTask(String targetTaskId) { - Node n = idToNode.get(targetTaskId); - if (n == null) { - return 0; - } - return n.indent; - } - - public void initialize(TaskListMetadata list, Filter filter) { - initializeFromSerializedTree(list, filter, getSerializedTree(list)); - } - - public void initializeFromSerializedTree(TaskListMetadata list, Filter filter, String serializedTree) { - idToNode.clear(); - treeRoot = buildTreeModel(serializedTree, node -> idToNode.put(node.uuid, node)); - verifyTreeModel(list, filter); - } - - private void verifyTreeModel(TaskListMetadata list, Filter filter) { - boolean changedThings = false; - Set keySet = idToNode.keySet(); - Set currentIds = new HashSet<>(); - for (String id : keySet) { - currentIds.add(id); - } - Set idsInQuery = new HashSet<>(); - String sql = filter.getSqlQuery().replaceAll("ORDER BY .*", ""); //$NON-NLS-1$//$NON-NLS-2$ - sql = sql + String.format(" ORDER BY %s", Task.CREATION_DATE); //$NON-NLS-1$ - TodorooCursor tasks = taskDao.fetchFiltered(sql, Task.UUID); - try { - for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) { - String id = tasks.getString(0); - idsInQuery.add(id); - if (idToNode.containsKey(id)) { - continue; - } - - changedThings = true; - Node newNode = new Node(id, treeRoot, 0); - treeRoot.children.add(0, newNode); - idToNode.put(id, newNode); - } - - currentIds.removeAll(idsInQuery); - if (currentIds.size() > 0) { - removeNodes(currentIds); - changedThings = true; - } - } finally { - tasks.close(); - } - if (changedThings) { - writeSerialization(list, serializeTree(), false); - } - } - - private void removeNodes(Set idsToRemove) { - for (String id : idsToRemove) { - Node node = idToNode.get(id); - if (node == null) { - continue; - } - - // Remove node from tree, put all children under parent - Node parent = node.parent; - parent.children.remove(node); - for (Node child : node.children) { - child.parent = parent; - parent.children.add(child); - setNodeIndent(child, parent.indent + 1); - } - } - } - - Node findNodeForTask(String taskId) { - return idToNode.get(taskId); - } - - private String[] getOrderedIds() { - ArrayList ids = new ArrayList<>(); - orderedIdHelper(treeRoot, ids); - return ids.toArray(new String[ids.size()]); - } - - String getOrderString() { - String[] ids = getOrderedIds(); - return buildOrderString(ids); - } - - public static String buildOrderString(String[] ids) { - StringBuilder builder = new StringBuilder(); - if (ids.length == 0) { - return "(1)"; //$NON-NLS-1$ - } - for (int i = ids.length - 1; i >= 0; i--) { - builder.append(Task.UUID.eq(ids[i]).toString()); - if (i > 0) { - builder.append(", "); //$NON-NLS-1$ - } - } - return builder.toString(); - } - - private void orderedIdHelper(Node node, List ids) { - if (node != treeRoot) { - ids.add(node.uuid); - } - - for (Node child : node.children) { - orderedIdHelper(child, ids); - } - } - - public void applyToDescendants(String taskId, OrderedListNodeVisitor visitor) { - Node n = idToNode.get(taskId); - if (n == null) { - return; - } - applyToDescendantsHelper(n, visitor); - } - - private void applyToDescendantsHelper(Node n, OrderedListNodeVisitor visitor) { - ArrayList children = n.children; - for (Node child : children) { - visitor.visitNode(child); - applyToDescendantsHelper(child, visitor); - } - } - - public void indent(TaskListMetadata list, Filter filter, String targetTaskId, int delta) { - Node node = idToNode.get(targetTaskId); - indentHelper(list, filter, node, delta); - } - - private void indentHelper(TaskListMetadata list, Filter filter, Node node, int delta) { - if (node == null) { - return; - } - if (delta == 0) { - return; - } - Node parent = node.parent; - if (parent == null) { - return; - } - - if (delta > 0) { - ArrayList siblings = parent.children; - int index = siblings.indexOf(node); - if (index <= 0) // Can't indent first child - { - return; - } - Node newParent = siblings.get(index - 1); - siblings.remove(index); - node.parent = newParent; - newParent.children.add(node); - setNodeIndent(node, newParent.indent + 1); - } else if (delta < 0) { - if (parent == treeRoot) // Can't deindent a top level item - { - return; - } - - ArrayList siblings = parent.children; - int index = siblings.indexOf(node); - if (index < 0) { - return; - } - - Node newParent = parent.parent; - ArrayList newSiblings = newParent.children; - int insertAfter = newSiblings.indexOf(parent); - siblings.remove(index); - node.parent = newParent; - setNodeIndent(node, newParent.indent + 1); - newSiblings.add(insertAfter + 1, node); - } - - writeSerialization(list, serializeTree(), true); - applyToFilter(filter); - } - - private void setNodeIndent(Node node, int indent) { - node.indent = indent; - adjustDescendantsIndent(node, indent); - } - - private void adjustDescendantsIndent(Node node, int baseIndent) { - for (Node child : node.children) { - child.indent = baseIndent + 1; - adjustDescendantsIndent(child, child.indent); - } - } - - public void moveTo(TaskListMetadata list, Filter filter, String targetTaskId, String beforeTaskId) { - Node target = idToNode.get(targetTaskId); - if (target == null) { - return; - } - - if ("-1".equals(beforeTaskId)) { //$NON-NLS-1$ - moveToEndOfList(list, filter, target); - return; - } - - Node before = idToNode.get(beforeTaskId); - - if (before == null) { - return; - } - - if (isDescendantOf(before, target)) { - return; - } - - moveHelper(list, filter, target, before); - } - - public void moveToParentOf(String moveThis, String toParentOfThis) { - Node target = idToNode.get(toParentOfThis); - if (target == null) { - return; - } - - Node toMove = idToNode.get(moveThis); - if (toMove == null) { - return; - } - - Node newParent = target.parent; - Node oldParent = toMove.parent; - - oldParent.children.remove(toMove); - toMove.parent = newParent; - newParent.children.add(toMove); - setNodeIndent(toMove, toMove.parent.indent + 1); - } - - private void moveHelper(TaskListMetadata list, Filter filter, Node moveThis, Node beforeThis) { - Node oldParent = moveThis.parent; - ArrayList oldSiblings = oldParent.children; - - Node newParent = beforeThis.parent; - ArrayList newSiblings = newParent.children; - - int beforeIndex = newSiblings.indexOf(beforeThis); - if (beforeIndex < 0) { - return; - } - - int nodeIndex = oldSiblings.indexOf(moveThis); - if (nodeIndex < 0) { - return; - } - - moveThis.parent = newParent; - setNodeIndent(moveThis, newParent.indent + 1); - oldSiblings.remove(moveThis); - - if (newSiblings == oldSiblings && beforeIndex > nodeIndex) { - beforeIndex--; - } - newSiblings.add(beforeIndex, moveThis); - writeSerialization(list, serializeTree(), true); - applyToFilter(filter); - } - - // Returns true if desc is a descendant of parent - private boolean isDescendantOf(Node desc, Node parent) { - Node curr = desc; - while (curr != treeRoot) { - if (curr == parent) { - return true; - } - curr = curr.parent; - } - return false; - } - - private void moveToEndOfList(TaskListMetadata list, Filter filter, Node moveThis) { - Node parent = moveThis.parent; - parent.children.remove(moveThis); - treeRoot.children.add(moveThis); - moveThis.parent = treeRoot; - setNodeIndent(moveThis, 0); - writeSerialization(list, serializeTree(), true); - applyToFilter(filter); - } - - public void onCreateTask(TaskListMetadata list, Filter filter, String uuid) { - if (idToNode.containsKey(uuid) || !RemoteModel.isValidUuid(uuid)) { - return; - } - - Node newNode = new Node(uuid, treeRoot, 0); - treeRoot.children.add(0, newNode); - idToNode.put(uuid, newNode); - writeSerialization(list, serializeTree(), true); - applyToFilter(filter); - } - - public void onDeleteTask(TaskListMetadata list, Filter filter, String taskId) { - Node task = idToNode.get(taskId); - if (task == null) { - return; - } - - Node parent = task.parent; - ArrayList siblings = parent.children; - int index = siblings.indexOf(task); - - if (index >= 0) { - siblings.remove(index); - } - for (Node child : task.children) { - child.parent = parent; - siblings.add(index, child); - setNodeIndent(child, parent.indent + 1); - index++; - } - idToNode.remove(taskId); - - writeSerialization(list, serializeTree(), true); - applyToFilter(filter); - } - - private interface JSONTreeModelBuilder { - void afterAddNode(Node node); - } - - public static Node buildTreeModel(String serializedTree, JSONTreeModelBuilder callback) { - Node root = new Node("-1", null, -1); //$NON-NLS-1$ - try { - JSONArray tree = new JSONArray(serializedTree); - recursivelyBuildChildren(root, tree, callback); - } catch (JSONException e) { - Timber.e(e, e.getMessage()); - } - return root; - } - - private static void recursivelyBuildChildren(Node node, JSONArray children, JSONTreeModelBuilder callback) throws JSONException { - for (int i = 1; i < children.length(); i++) { - JSONArray subarray = children.optJSONArray(i); - String uuid; - if (subarray == null) { - uuid = children.getString(i); - } else { - uuid = subarray.getString(0); - } - - Node child = new Node(uuid, node, node.indent + 1); - if (subarray != null) { - recursivelyBuildChildren(child, subarray, callback); - } - node.children.add(child); - if (callback != null) { - callback.afterAddNode(child); - } - } - } - - String serializeTree() { - return serializeTree(treeRoot); - } - - public static String serializeTree(Node root) { - JSONArray tree = new JSONArray(); - if (root == null) { - return tree.toString(); - } - - recursivelySerialize(root, tree); - return tree.toString(); - } - - private static void recursivelySerialize(Node node, JSONArray serializeTo) { - ArrayList children = node.children; - serializeTo.put(node.uuid); - for (Node child : children) { - if (child.children.size() > 0) { - JSONArray branch = new JSONArray(); - recursivelySerialize(child, branch); - serializeTo.put(branch); - } else { - serializeTo.put(child.uuid); - } - } - } -} diff --git a/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java b/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java index a3884c43c..81333f2d4 100644 --- a/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java +++ b/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java @@ -2,31 +2,45 @@ package com.todoroo.astrid.subtasks; import android.text.TextUtils; +import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.sql.Criterion; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskListMetadataDao; +import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.TaskListMetadata; +import org.json.JSONArray; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import javax.inject.Inject; -public class SubtasksFilterUpdater extends AstridOrderedListUpdater { +import timber.log.Timber; + +public class SubtasksFilterUpdater { public static final String ACTIVE_TASKS_ORDER = "active_tasks_order"; //$NON-NLS-1$ public static final String TODAY_TASKS_ORDER = "today_tasks_order"; //$NON-NLS-1$ private final TaskListMetadataDao taskListMetadataDao; + private final TaskDao taskDao; + private Node treeRoot; + private final HashMap idToNode = new HashMap<>(); @Inject public SubtasksFilterUpdater(TaskListMetadataDao taskListMetadataDao, TaskDao taskDao) { - super(taskDao); - + this.taskDao = taskDao; this.taskListMetadataDao = taskListMetadataDao; } - @Override protected String getSerializedTree(TaskListMetadata list) { if (list == null) { return "[]"; //$NON-NLS-1$ @@ -40,7 +54,6 @@ public class SubtasksFilterUpdater extends AstridOrderedListUpdater { return order; } - @Override protected void writeSerialization(TaskListMetadata list, String serialized, boolean shouldQueueSync) { if (list != null) { list.setTaskIDs(serialized); @@ -51,13 +64,11 @@ public class SubtasksFilterUpdater extends AstridOrderedListUpdater { } } - @Override public void initialize(TaskListMetadata list, Filter filter) { - super.initialize(list, filter); + initializeFromSerializedTree(list, filter, getSerializedTree(list)); applyToFilter(filter); } - @Override protected void applyToFilter(Filter filter) { String query = filter.getSqlQuery(); @@ -69,4 +80,408 @@ public class SubtasksFilterUpdater extends AstridOrderedListUpdater { filter.setFilterQueryOverride(query); } + + public interface OrderedListNodeVisitor { + void visitNode(Node node); + } + + public static class Node { + public String uuid; + public Node parent; + public int indent; + public final ArrayList children = new ArrayList<>(); + + public Node(String uuid, Node parent, int indent) { + this.uuid = uuid; + this.parent = parent; + this.indent = indent; + } + } + + public int getIndentForTask(String targetTaskId) { + Node n = idToNode.get(targetTaskId); + if (n == null) { + return 0; + } + return n.indent; + } + + public void initializeFromSerializedTree(TaskListMetadata list, Filter filter, String serializedTree) { + idToNode.clear(); + treeRoot = buildTreeModel(serializedTree, node -> idToNode.put(node.uuid, node)); + verifyTreeModel(list, filter); + } + + private void verifyTreeModel(TaskListMetadata list, Filter filter) { + boolean changedThings = false; + Set keySet = idToNode.keySet(); + Set currentIds = new HashSet<>(); + for (String id : keySet) { + currentIds.add(id); + } + Set idsInQuery = new HashSet<>(); + String sql = filter.getSqlQuery().replaceAll("ORDER BY .*", ""); //$NON-NLS-1$//$NON-NLS-2$ + sql = sql + String.format(" ORDER BY %s", Task.CREATION_DATE); //$NON-NLS-1$ + TodorooCursor tasks = taskDao.fetchFiltered(sql, Task.UUID); + try { + for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) { + String id = tasks.getString(0); + idsInQuery.add(id); + if (idToNode.containsKey(id)) { + continue; + } + + changedThings = true; + Node newNode = new Node(id, treeRoot, 0); + treeRoot.children.add(0, newNode); + idToNode.put(id, newNode); + } + + currentIds.removeAll(idsInQuery); + if (currentIds.size() > 0) { + removeNodes(currentIds); + changedThings = true; + } + } finally { + tasks.close(); + } + if (changedThings) { + writeSerialization(list, serializeTree(), false); + } + } + + private void removeNodes(Set idsToRemove) { + for (String id : idsToRemove) { + Node node = idToNode.get(id); + if (node == null) { + continue; + } + + // Remove node from tree, put all children under parent + Node parent = node.parent; + parent.children.remove(node); + for (Node child : node.children) { + child.parent = parent; + parent.children.add(child); + setNodeIndent(child, parent.indent + 1); + } + } + } + + Node findNodeForTask(String taskId) { + return idToNode.get(taskId); + } + + private String[] getOrderedIds() { + ArrayList ids = new ArrayList<>(); + orderedIdHelper(treeRoot, ids); + return ids.toArray(new String[ids.size()]); + } + + String getOrderString() { + String[] ids = getOrderedIds(); + return buildOrderString(ids); + } + + public static String buildOrderString(String[] ids) { + StringBuilder builder = new StringBuilder(); + if (ids.length == 0) { + return "(1)"; //$NON-NLS-1$ + } + for (int i = ids.length - 1; i >= 0; i--) { + builder.append(Task.UUID.eq(ids[i]).toString()); + if (i > 0) { + builder.append(", "); //$NON-NLS-1$ + } + } + return builder.toString(); + } + + private void orderedIdHelper(Node node, List ids) { + if (node != treeRoot) { + ids.add(node.uuid); + } + + for (Node child : node.children) { + orderedIdHelper(child, ids); + } + } + + public void applyToDescendants(String taskId, OrderedListNodeVisitor visitor) { + Node n = idToNode.get(taskId); + if (n == null) { + return; + } + applyToDescendantsHelper(n, visitor); + } + + private void applyToDescendantsHelper(Node n, OrderedListNodeVisitor visitor) { + ArrayList children = n.children; + for (Node child : children) { + visitor.visitNode(child); + applyToDescendantsHelper(child, visitor); + } + } + + public void indent(TaskListMetadata list, Filter filter, String targetTaskId, int delta) { + Node node = idToNode.get(targetTaskId); + indentHelper(list, filter, node, delta); + } + + private void indentHelper(TaskListMetadata list, Filter filter, Node node, int delta) { + if (node == null) { + return; + } + if (delta == 0) { + return; + } + Node parent = node.parent; + if (parent == null) { + return; + } + + if (delta > 0) { + ArrayList siblings = parent.children; + int index = siblings.indexOf(node); + if (index <= 0) // Can't indent first child + { + return; + } + Node newParent = siblings.get(index - 1); + siblings.remove(index); + node.parent = newParent; + newParent.children.add(node); + setNodeIndent(node, newParent.indent + 1); + } else if (delta < 0) { + if (parent == treeRoot) // Can't deindent a top level item + { + return; + } + + ArrayList siblings = parent.children; + int index = siblings.indexOf(node); + if (index < 0) { + return; + } + + Node newParent = parent.parent; + ArrayList newSiblings = newParent.children; + int insertAfter = newSiblings.indexOf(parent); + siblings.remove(index); + node.parent = newParent; + setNodeIndent(node, newParent.indent + 1); + newSiblings.add(insertAfter + 1, node); + } + + writeSerialization(list, serializeTree(), true); + applyToFilter(filter); + } + + private void setNodeIndent(Node node, int indent) { + node.indent = indent; + adjustDescendantsIndent(node, indent); + } + + private void adjustDescendantsIndent(Node node, int baseIndent) { + for (Node child : node.children) { + child.indent = baseIndent + 1; + adjustDescendantsIndent(child, child.indent); + } + } + + public void moveTo(TaskListMetadata list, Filter filter, String targetTaskId, String beforeTaskId) { + Node target = idToNode.get(targetTaskId); + if (target == null) { + return; + } + + if ("-1".equals(beforeTaskId)) { //$NON-NLS-1$ + moveToEndOfList(list, filter, target); + return; + } + + Node before = idToNode.get(beforeTaskId); + + if (before == null) { + return; + } + + if (isDescendantOf(before, target)) { + return; + } + + moveHelper(list, filter, target, before); + } + + public void moveToParentOf(String moveThis, String toParentOfThis) { + Node target = idToNode.get(toParentOfThis); + if (target == null) { + return; + } + + Node toMove = idToNode.get(moveThis); + if (toMove == null) { + return; + } + + Node newParent = target.parent; + Node oldParent = toMove.parent; + + oldParent.children.remove(toMove); + toMove.parent = newParent; + newParent.children.add(toMove); + setNodeIndent(toMove, toMove.parent.indent + 1); + } + + private void moveHelper(TaskListMetadata list, Filter filter, Node moveThis, Node beforeThis) { + Node oldParent = moveThis.parent; + ArrayList oldSiblings = oldParent.children; + + Node newParent = beforeThis.parent; + ArrayList newSiblings = newParent.children; + + int beforeIndex = newSiblings.indexOf(beforeThis); + if (beforeIndex < 0) { + return; + } + + int nodeIndex = oldSiblings.indexOf(moveThis); + if (nodeIndex < 0) { + return; + } + + moveThis.parent = newParent; + setNodeIndent(moveThis, newParent.indent + 1); + oldSiblings.remove(moveThis); + + if (newSiblings == oldSiblings && beforeIndex > nodeIndex) { + beforeIndex--; + } + newSiblings.add(beforeIndex, moveThis); + writeSerialization(list, serializeTree(), true); + applyToFilter(filter); + } + + // Returns true if desc is a descendant of parent + private boolean isDescendantOf(Node desc, Node parent) { + Node curr = desc; + while (curr != treeRoot) { + if (curr == parent) { + return true; + } + curr = curr.parent; + } + return false; + } + + private void moveToEndOfList(TaskListMetadata list, Filter filter, Node moveThis) { + Node parent = moveThis.parent; + parent.children.remove(moveThis); + treeRoot.children.add(moveThis); + moveThis.parent = treeRoot; + setNodeIndent(moveThis, 0); + writeSerialization(list, serializeTree(), true); + applyToFilter(filter); + } + + public void onCreateTask(TaskListMetadata list, Filter filter, String uuid) { + if (idToNode.containsKey(uuid) || !RemoteModel.isValidUuid(uuid)) { + return; + } + + Node newNode = new Node(uuid, treeRoot, 0); + treeRoot.children.add(0, newNode); + idToNode.put(uuid, newNode); + writeSerialization(list, serializeTree(), true); + applyToFilter(filter); + } + + public void onDeleteTask(TaskListMetadata list, Filter filter, String taskId) { + Node task = idToNode.get(taskId); + if (task == null) { + return; + } + + Node parent = task.parent; + ArrayList siblings = parent.children; + int index = siblings.indexOf(task); + + if (index >= 0) { + siblings.remove(index); + } + for (Node child : task.children) { + child.parent = parent; + siblings.add(index, child); + setNodeIndent(child, parent.indent + 1); + index++; + } + idToNode.remove(taskId); + + writeSerialization(list, serializeTree(), true); + applyToFilter(filter); + } + + private interface JSONTreeModelBuilder { + void afterAddNode(Node node); + } + + public static Node buildTreeModel(String serializedTree, JSONTreeModelBuilder callback) { + Node root = new Node("-1", null, -1); //$NON-NLS-1$ + try { + JSONArray tree = new JSONArray(serializedTree); + recursivelyBuildChildren(root, tree, callback); + } catch (JSONException e) { + Timber.e(e, e.getMessage()); + } + return root; + } + + private static void recursivelyBuildChildren(Node node, JSONArray children, JSONTreeModelBuilder callback) throws JSONException { + for (int i = 1; i < children.length(); i++) { + JSONArray subarray = children.optJSONArray(i); + String uuid; + if (subarray == null) { + uuid = children.getString(i); + } else { + uuid = subarray.getString(0); + } + + Node child = new Node(uuid, node, node.indent + 1); + if (subarray != null) { + recursivelyBuildChildren(child, subarray, callback); + } + node.children.add(child); + if (callback != null) { + callback.afterAddNode(child); + } + } + } + + String serializeTree() { + return serializeTree(treeRoot); + } + + public static String serializeTree(Node root) { + JSONArray tree = new JSONArray(); + if (root == null) { + return tree.toString(); + } + + recursivelySerialize(root, tree); + return tree.toString(); + } + + private static void recursivelySerialize(Node node, JSONArray serializeTo) { + ArrayList children = node.children; + serializeTo.put(node.uuid); + for (Node child : children) { + if (child.children.size() > 0) { + JSONArray branch = new JSONArray(); + recursivelySerialize(child, branch); + serializeTo.put(branch); + } else { + serializeTo.put(child.uuid); + } + } + } } diff --git a/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java b/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java index 2bb3a5af3..793afe3fa 100644 --- a/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java +++ b/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java @@ -17,7 +17,7 @@ import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.TaskListMetadata; -import com.todoroo.astrid.subtasks.AstridOrderedListUpdater.Node; +import com.todoroo.astrid.subtasks.SubtasksFilterUpdater.Node; import org.tasks.R; import org.tasks.injection.ForApplication; @@ -92,7 +92,7 @@ public class SubtasksHelper { serialized = "[]"; //$NON-NLS-1$ } - return AstridOrderedListUpdater.buildOrderString(getStringIdArray(serialized)); + return SubtasksFilterUpdater.buildOrderString(getStringIdArray(serialized)); } @Deprecated @@ -130,9 +130,9 @@ public class SubtasksHelper { HashMap idMap = getIdMap(taskDao, localIds, Task.ID, Task.UUID); idMap.put(-1L, "-1"); //$NON-NLS-1$ - Node tree = AstridOrderedListUpdater.buildTreeModel(localTree, null); + Node tree = SubtasksFilterUpdater.buildTreeModel(localTree, null); remapLocalTreeToRemote(tree, idMap); - return AstridOrderedListUpdater.serializeTree(tree); + return SubtasksFilterUpdater.serializeTree(tree); } public interface TreeRemapHelper { @@ -182,5 +182,4 @@ public class SubtasksHelper { } return map; } - }