Refactored GtasksTaskListUpdater into OrderedListUpdater to create a new child class

pull/14/head
Tim Su 12 years ago
parent d6345b4b32
commit 57f3a70089

@ -31,6 +31,9 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
/** Property reading visitor */
private static final CursorReadingVisitor reader = new CursorReadingVisitor();
/** Wrapped cursor */
private final Cursor cursor;
/**
* Create an <code>AstridCursor</code> from the supplied {@link Cursor}
* object.
@ -41,6 +44,7 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
public TodorooCursor(Cursor cursor, Property<?>[] properties) {
super(cursor);
this.cursor = cursor;
this.properties = properties;
columnIndexCache = new WeakHashMap<String, Integer>();
}
@ -56,6 +60,13 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
return (PROPERTY_TYPE)property.accept(reader, this);
}
/**
* @return underlying cursor
*/
public Cursor getCursor() {
return cursor;
}
/**
* Gets entire property list
* @return

@ -421,10 +421,15 @@ public class TouchListView extends ListView {
if(Thread.currentThread().isInterrupted())
return;
if(mDragPos == mFirstDragPos &&
if(mDragView != null && mDragPos == mFirstDragPos &&
Math.abs(mDragCurrentX - mDragStartX) < 10) {
stopDragging(null);
mClickListener.onLongClick(mOriginalView);
post(new Runnable() {
public void run() {
stopDragging(null);
mClickListener.onLongClick(mOriginalView);
invalidate();
}
});
}
}
};
@ -461,6 +466,7 @@ public class TouchListView extends ListView {
Context.WINDOW_SERVICE);
wm.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
if (ev != null && mClickListener != null) {
// detect press & long press case
@ -473,7 +479,6 @@ public class TouchListView extends ListView {
mClickListener.onLongClick(mOriginalView);
}
}
mDragView = null;
}
if(longPressThread != null) {

@ -97,9 +97,9 @@ public class GtasksListFragment extends DraggableTaskListFragment {
long destinationTaskId = taskAdapter.getItemId(to);
if(to == getListView().getCount() - 1)
gtasksTaskListUpdater.moveTo(targetTaskId, -1);
gtasksTaskListUpdater.moveTo(filter, list, targetTaskId, -1);
else
gtasksTaskListUpdater.moveTo(targetTaskId, destinationTaskId);
gtasksTaskListUpdater.moveTo(filter, list, targetTaskId, destinationTaskId);
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.getTaskMetadata(targetTaskId));
loadTaskListContent(true);
}
@ -109,7 +109,7 @@ public class GtasksListFragment extends DraggableTaskListFragment {
@Override
public void swipeRight(int which) {
long targetTaskId = taskAdapter.getItemId(which);
gtasksTaskListUpdater.indent(targetTaskId, 1);
gtasksTaskListUpdater.indent(filter, list, targetTaskId, 1);
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.getTaskMetadata(targetTaskId));
loadTaskListContent(true);
}
@ -117,7 +117,7 @@ public class GtasksListFragment extends DraggableTaskListFragment {
@Override
public void swipeLeft(int which) {
long targetTaskId = taskAdapter.getItemId(which);
gtasksTaskListUpdater.indent(targetTaskId, -1);
gtasksTaskListUpdater.indent(filter, list, targetTaskId, -1);
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.getTaskMetadata(targetTaskId));
loadTaskListContent(true);
}

@ -27,6 +27,7 @@ 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.OrderedListUpdater.OrderedListIterator;
import com.todoroo.astrid.sync.SyncMetadataService;
import com.todoroo.astrid.sync.SyncProviderUtilities;
@ -128,17 +129,13 @@ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskC
// --- list iterating helpers
public interface ListIterator {
public void processTask(long taskId, Metadata metadata);
}
public void iterateThroughList(StoreObject list, ListIterator iterator) {
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, ListIterator iterator, long startAtOrder, boolean reverse) {
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) :
@ -195,7 +192,7 @@ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskC
final AtomicLong parentToMatch = new AtomicLong(gtasksMetadata.getValue(GtasksMetadata.PARENT_TASK).longValue());
final AtomicReference<String> sibling = new AtomicReference<String>();
ListIterator iterator = new ListIterator() {
OrderedListIterator iterator = new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
Task t = taskDao.fetch(taskId, Task.TITLE, Task.DELETION_DATE);

@ -1,33 +1,28 @@
package com.todoroo.astrid.gtasks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import android.text.TextUtils;
import android.util.Log;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.subtasks.OrderedListUpdater;
public class GtasksTaskListUpdater {
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private MetadataDao metadataDao;
public class GtasksTaskListUpdater extends OrderedListUpdater<StoreObject> {
/** map of task -> parent task */
final HashMap<Long, Long> parents = new HashMap<Long, Long>();
@ -38,251 +33,63 @@ public class GtasksTaskListUpdater {
final HashMap<Long, String> localToRemoteIdMap =
new HashMap<Long, String>();
public GtasksTaskListUpdater() {
DependencyInjectionService.getInstance().inject(this);
}
// --- task indenting
/**
* Indent a task and all its children
*/
public void indent(final long targetTaskId, final int delta) {
Metadata targetMetadata = gtasksMetadataService.getTaskMetadata(targetTaskId);
if(targetMetadata == null)
return;
StoreObject list = gtasksListService.getList(targetMetadata.getValue(GtasksMetadata.LIST_ID));
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);
final AtomicReference<StoreObject> listRef = new AtomicReference<StoreObject>(list);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.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);
long newParent = computeNewParent(listRef.get(), taskId, indent + delta - 1);
if (newParent == taskId) {
metadata.setValue(GtasksMetadata.PARENT_TASK, Task.NO_ID);
} else {
metadata.setValue(GtasksMetadata.PARENT_TASK, newParent);
}
saveAndUpdateModifiedDate(metadata, taskId);
}
} 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);
saveAndUpdateModifiedDate(metadata, taskId);
}
} else {
previousIndent.set(indent);
previousTask.set(taskId);
}
}
});
}
/**
* Helper function to iterate through a list and compute a new parent for the target task
* based on the target parent's indent
* @param list
* @param targetTaskId
* @param newIndent
* @return
*/
private long computeNewParent(StoreObject list, long targetTaskId, int targetParentIndent) {
final AtomicInteger desiredParentIndent = new AtomicInteger(targetParentIndent);
final AtomicLong targetTask = new AtomicLong(targetTaskId);
final AtomicLong lastPotentialParent = new AtomicLong(-1);
final AtomicBoolean computedParent = new AtomicBoolean(false);
GtasksMetadataService.ListIterator iterator = new GtasksMetadataService.ListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if (targetTask.get() == taskId) {
computedParent.set(true);
}
int indent = metadata.getValue(GtasksMetadata.INDENT);
if (!computedParent.get() && indent == desiredParentIndent.get()) {
lastPotentialParent.set(taskId);
}
}
};
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private MetadataDao metadataDao;
gtasksMetadataService.iterateThroughList(list, iterator);
if (lastPotentialParent.get() == -1) return Task.NO_ID;
return lastPotentialParent.get();
public GtasksTaskListUpdater() {
super();
}
// --- task moving
// --- overrides
private static class Node {
public final long taskId;
public Node parent;
public final ArrayList<Node> children = new ArrayList<Node>();
public Node(long taskId, Node parent) {
this.taskId = taskId;
this.parent = parent;
}
@Override
protected IntegerProperty indentProperty() {
return GtasksMetadata.INDENT;
}
/**
* Move a task and all its children to the position right above
* taskIdToMoveto. Will change the indent level to match taskIdToMoveTo.
*
* @param newTaskId task we will move above. if -1, moves to end of list
*/
public void moveTo(final long targetTaskId, final long moveBeforeTaskId) {
Metadata targetMetadata = gtasksMetadataService.getTaskMetadata(targetTaskId);
if(targetMetadata == null)
return;
StoreObject list = gtasksListService.getList(targetMetadata.getValue(GtasksMetadata.LIST_ID));
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
return;
Node root = buildTreeModel(list);
Node target = findNode(root, targetTaskId);
if(target != null && target.parent != null) {
if(moveBeforeTaskId == -1) {
target.parent.children.remove(target);
root.children.add(target);
target.parent = root;
} else {
Node sibling = findNode(root, moveBeforeTaskId);
if(sibling != null) {
int index = sibling.parent.children.indexOf(sibling);
target.parent.children.remove(target);
sibling.parent.children.add(index, target);
target.parent = sibling.parent;
}
}
}
traverseTreeAndWriteValues(root, new AtomicLong(0), -1);
@Override
protected LongProperty orderProperty() {
return GtasksMetadata.ORDER;
}
private void traverseTreeAndWriteValues(Node node, AtomicLong order, int indent) {
if(node.taskId != -1) {
Metadata metadata = gtasksMetadataService.getTaskMetadata(node.taskId);
if(metadata == null)
metadata = GtasksMetadata.createEmptyMetadata(node.taskId);
metadata.setValue(GtasksMetadata.ORDER, order.getAndIncrement());
metadata.setValue(GtasksMetadata.INDENT, indent);
metadata.setValue(GtasksMetadata.PARENT_TASK, node.parent.taskId);
saveAndUpdateModifiedDate(metadata, node.taskId);
}
for(Node child : node.children) {
traverseTreeAndWriteValues(child, order, indent + 1);
}
@Override
protected LongProperty parentProperty() {
return GtasksMetadata.PARENT_TASK;
}
private Node findNode(Node node, long taskId) {
if(node.taskId == taskId)
return node;
for(Node child : node.children) {
Node found = findNode(child, taskId);
if(found != null)
return found;
}
return null;
@Override
protected Metadata getTaskMetadata(StoreObject list, long taskId) {
return gtasksMetadataService.getTaskMetadata(taskId);
}
private Node buildTreeModel(StoreObject list) {
final Node root = new Node(-1, null);
final AtomicInteger previoustIndent = new AtomicInteger(-1);
final AtomicReference<Node> currentNode = new AtomicReference<Node>(root);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(GtasksMetadata.INDENT);
int previousIndentValue = previoustIndent.get();
if(indent == previousIndentValue) { // sibling
Node parent = currentNode.get().parent;
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else if(indent > previousIndentValue) { // child
Node parent = currentNode.get();
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else { // in a different tree
Node node = currentNode.get().parent;
for(int i = indent; i < previousIndentValue; i++)
node = node.parent;
if(node == null)
node = root;
currentNode.set(new Node(taskId, node));
node.children.add(currentNode.get());
}
previoustIndent.set(indent);
}
});
return root;
@Override
protected Metadata createEmptyMetadata(StoreObject list, long taskId) {
Metadata metadata = GtasksMetadata.createEmptyMetadata(taskId);
metadata.setValue(GtasksMetadata.LIST_ID, list.getValue(GtasksList.REMOTE_ID));
return metadata;
}
// --- utility
public void debugPrint(String listId) {
StoreObject list = gtasksListService.getList(listId);
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
return;
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
public void processTask(long taskId, Metadata metadata) {
System.err.format("id %d: order %d, indent:%d, parent:%d\n", taskId, //$NON-NLS-1$
metadata.getValue(GtasksMetadata.ORDER),
metadata.getValue(GtasksMetadata.INDENT),
metadata.getValue(GtasksMetadata.PARENT_TASK));
}
});
@Override
protected void beforeIndent(StoreObject list) {
updateParentSiblingMapsFor(list);
}
@SuppressWarnings("nls")
public void debugPrint(Node root, int depth) {
for(int i = 0; i < depth; i++) System.err.print(" + ");
System.err.format("%03d", root.taskId);
System.err.print("\n");
for(int i = 0; i < root.children.size(); i++)
debugPrint(root.children.get(i), depth + 1);
@Override
protected void iterateThroughList(Filter filter, StoreObject list, OrderedListIterator iterator) {
gtasksMetadataService.iterateThroughList(list, iterator);
}
private final Task taskContainer = new Task();
// --- used during synchronization
private void saveAndUpdateModifiedDate(Metadata metadata, long taskId) {
if(metadata.getSetValues().size() == 0)
return;
PluginServices.getMetadataService().save(metadata);
taskContainer.setId(taskId);
taskContainer.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
taskContainer.setValue(Task.DETAILS_DATE, DateUtilities.now());
PluginServices.getTaskService().save(taskContainer);
/**
* Create a local tree of tasks to expedite sibling and parent lookups
*/
public void createParentSiblingMaps() {
for(StoreObject list : gtasksListService.getLists()) {
updateParentSiblingMapsFor(list);
}
}
// --- used during synchronization
/**
* Update order, parent, and indentation fields for all tasks in the given list
* @param listId
@ -297,7 +104,7 @@ public class GtasksTaskListUpdater {
final AtomicLong order = new AtomicLong(0);
final AtomicInteger previousIndent = new AtomicInteger(-1);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
gtasksMetadataService.iterateThroughList(list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
metadata.setValue(GtasksMetadata.ORDER, order.getAndAdd(1));
@ -323,7 +130,8 @@ public class GtasksTaskListUpdater {
private void orderAndIndentHelper(String listId, AtomicLong order, long parent, int indentLevel) {
TodorooCursor<Metadata> metadata = metadataDao.query(Query.select(Metadata.PROPERTIES)
.where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), GtasksMetadata.LIST_ID.eq(listId), GtasksMetadata.PARENT_TASK.eq(parent)))
.where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY),
GtasksMetadata.LIST_ID.eq(listId), GtasksMetadata.PARENT_TASK.eq(parent)))
.orderBy(Order.asc(GtasksMetadata.GTASKS_ORDER)));
try {
if (metadata.getCount() > 0) {
@ -342,20 +150,11 @@ public class GtasksTaskListUpdater {
}
}
/**
* Create a local tree of tasks to expedite sibling and parent lookups
*/
public void createParentSiblingMaps() {
for(StoreObject list : gtasksListService.getLists()) {
updateParentSiblingMapsFor(list);
}
}
private void updateParentSiblingMapsFor(StoreObject list) {
final AtomicLong previousTask = new AtomicLong(-1L);
final AtomicInteger previousIndent = new AtomicInteger(-1);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
gtasksMetadataService.iterateThroughList(list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(GtasksMetadata.INDENT);

@ -0,0 +1,288 @@
package com.todoroo.astrid.subtasks;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
abstract public class OrderedListUpdater<LIST> {
public OrderedListUpdater() {
DependencyInjectionService.getInstance().inject(this);
}
public interface OrderedListIterator {
public void processTask(long taskId, Metadata metadata);
}
// --- abstract
abstract protected Metadata getTaskMetadata(LIST list, long taskId);
abstract protected IntegerProperty indentProperty();
abstract protected LongProperty orderProperty();
abstract protected LongProperty parentProperty();
abstract protected void iterateThroughList(Filter filter, LIST list, OrderedListIterator iterator);
abstract protected Metadata createEmptyMetadata(LIST list, long taskId);
/** @param list */
protected void beforeIndent(LIST list) {
//
}
/**
* @param list
* @param taskId
* @param metadata
* @param indent
* @param order
*/
protected void beforeSaveIndent(LIST list, long taskId, Metadata metadata, int indent, int order) {
//
}
// --- task indenting
/**
* Indent a task and all its children
*/
public void indent(final Filter filter, final LIST list, final long targetTaskId, final int delta) {
if(list == null)
return;
beforeIndent(list);
final AtomicInteger targetTaskIndent = new AtomicInteger(-1);
final AtomicInteger previousIndent = new AtomicInteger(-1);
final AtomicLong previousTask = new AtomicLong(-1);
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(indentProperty());
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(indentProperty(), indent + delta);
if(parentProperty() != null) {
long newParent = computeNewParent(filter, list,
taskId, indent + delta - 1);
if (newParent == taskId)
metadata.setValue(parentProperty(), Task.NO_ID);
else
metadata.setValue(parentProperty(), newParent);
}
saveAndUpdateModifiedDate(metadata, taskId);
}
} else if(targetTaskIndent.get() > -1) {
// found first task that is not beneath target
if(indent <= targetTaskIndent.get())
targetTaskIndent.set(-1);
else {
metadata.setValue(indentProperty(), indent + delta);
saveAndUpdateModifiedDate(metadata, taskId);
}
} else {
previousIndent.set(indent);
previousTask.set(taskId);
}
}
});
}
/**
* Helper function to iterate through a list and compute a new parent for the target task
* based on the target parent's indent
* @param list
* @param targetTaskId
* @param newIndent
* @return
*/
private long computeNewParent(Filter filter, LIST list, long targetTaskId, int targetParentIndent) {
final AtomicInteger desiredParentIndent = new AtomicInteger(targetParentIndent);
final AtomicLong targetTask = new AtomicLong(targetTaskId);
final AtomicLong lastPotentialParent = new AtomicLong(-1);
final AtomicBoolean computedParent = new AtomicBoolean(false);
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if (targetTask.get() == taskId) {
computedParent.set(true);
}
int indent = metadata.getValue(indentProperty());
if (!computedParent.get() && indent == desiredParentIndent.get()) {
lastPotentialParent.set(taskId);
}
}
});
if (lastPotentialParent.get() == -1) return Task.NO_ID;
return lastPotentialParent.get();
}
// --- task moving
/**
* Move a task and all its children to the position right above
* taskIdToMoveto. Will change the indent level to match taskIdToMoveTo.
*
* @param newTaskId task we will move above. if -1, moves to end of list
*/
public void moveTo(Filter filter, LIST list, final long targetTaskId,
final long moveBeforeTaskId) {
if(list == null)
return;
Node root = buildTreeModel(filter, list);
Node target = findNode(root, targetTaskId);
if(target != null && target.parent != null) {
if(moveBeforeTaskId == -1) {
target.parent.children.remove(target);
root.children.add(target);
target.parent = root;
} else {
Node sibling = findNode(root, moveBeforeTaskId);
if(sibling != null) {
int index = sibling.parent.children.indexOf(sibling);
target.parent.children.remove(target);
sibling.parent.children.add(index, target);
target.parent = sibling.parent;
}
}
}
traverseTreeAndWriteValues(list, root, new AtomicLong(0), -1);
}
protected static class Node {
public final long taskId;
public Node parent;
public final ArrayList<Node> children = new ArrayList<Node>();
public Node(long taskId, Node parent) {
this.taskId = taskId;
this.parent = parent;
}
}
protected void traverseTreeAndWriteValues(LIST list, Node node, AtomicLong order, int indent) {
if(node.taskId != -1) {
Metadata metadata = getTaskMetadata(list, node.taskId);
if(metadata == null)
metadata = createEmptyMetadata(list, node.taskId);
metadata.setValue(orderProperty(), order.getAndIncrement());
metadata.setValue(indentProperty(), indent);
if(parentProperty() != null)
metadata.setValue(parentProperty(), node.parent.taskId);
saveAndUpdateModifiedDate(metadata, node.taskId);
}
for(Node child : node.children) {
traverseTreeAndWriteValues(list, child, order, indent + 1);
}
}
protected Node findNode(Node node, long taskId) {
if(node.taskId == taskId)
return node;
for(Node child : node.children) {
Node found = findNode(child, taskId);
if(found != null)
return found;
}
return null;
}
protected Node buildTreeModel(Filter filter, LIST list) {
final Node root = new Node(-1, null);
final AtomicInteger previoustIndent = new AtomicInteger(-1);
final AtomicReference<Node> currentNode = new AtomicReference<Node>(root);
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(indentProperty());
int previousIndentValue = previoustIndent.get();
if(indent == previousIndentValue) { // sibling
Node parent = currentNode.get().parent;
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else if(indent > previousIndentValue) { // child
Node parent = currentNode.get();
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else { // in a different tree
Node node = currentNode.get().parent;
for(int i = indent; i < previousIndentValue; i++)
node = node.parent;
if(node == null)
node = root;
currentNode.set(new Node(taskId, node));
node.children.add(currentNode.get());
}
previoustIndent.set(indent);
}
});
return root;
}
protected final Task taskContainer = new Task();
protected void saveAndUpdateModifiedDate(Metadata metadata, long taskId) {
if(metadata.getSetValues().size() == 0)
return;
PluginServices.getMetadataService().save(metadata);
taskContainer.setId(taskId);
taskContainer.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
taskContainer.setValue(Task.DETAILS_DATE, DateUtilities.now());
PluginServices.getTaskService().save(taskContainer);
}
// --- utility
public void debugPrint(Filter filter, LIST list) {
iterateThroughList(filter, list, new OrderedListIterator() {
public void processTask(long taskId, Metadata metadata) {
System.err.format("id %d: order %d, indent:%d, parent:%d\n", taskId, //$NON-NLS-1$
metadata.getValue(orderProperty()),
metadata.getValue(indentProperty()),
parentProperty() == null ? -1 : metadata.getValue(parentProperty()));
}
});
}
@SuppressWarnings("nls")
public void debugPrint(Node root, int depth) {
for(int i = 0; i < depth; i++) System.err.print(" + ");
System.err.format("%03d", root.taskId);
System.err.print("\n");
for(int i = 0; i < root.children.size(); i++)
debugPrint(root.children.get(i), depth + 1);
}
}

@ -15,12 +15,15 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
public class SubtasksListFragment extends TaskListFragment {
private final DisplayMetrics metrics = new DisplayMetrics();
private final SubtasksUpdater updater = new SubtasksUpdater();
public TouchListView getTouchListView() {
TouchListView tlv = (TouchListView) getListView();
return tlv;
@ -43,10 +46,39 @@ public class SubtasksListFragment extends TaskListFragment {
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
@SuppressWarnings("nls")
@Override
protected void setUpTaskList() {
String query = filter.sqlQuery;
query = String.format("LEFT JOIN %s ON (%s = %s AND %s = '%s') %s",
Metadata.TABLE, Task.ID, Metadata.TASK,
Metadata.KEY, SubtasksMetadata.METADATA_KEY, query);
query = query.replaceAll("ORDER BY .*", "");
query = query + String.format(" ORDER BY %s ASC, %s ASC",
SubtasksMetadata.ORDER, Task.ID);
filter.sqlQuery = query;
super.setUpTaskList();
}
private final DropListener dropListener = new DropListener() {
@Override
public void drop(int from, int to) {
System.err.println("DROPPED TO " + to);
long targetTaskId = taskAdapter.getItemId(from);
long destinationTaskId = taskAdapter.getItemId(to);
System.err.println("BEFORE");
updater.debugPrint(filter, SubtasksMetadata.LIST_ACTIVE_TASKS);
if(to == getListView().getCount() - 1)
updater.moveTo(filter, SubtasksMetadata.LIST_ACTIVE_TASKS, targetTaskId, -1);
else
updater.moveTo(filter, SubtasksMetadata.LIST_ACTIVE_TASKS, targetTaskId, destinationTaskId);
System.err.println("AFTER");
updater.debugPrint(filter, SubtasksMetadata.LIST_ACTIVE_TASKS);
loadTaskListContent(true);
}
};

@ -2,7 +2,6 @@ package com.todoroo.astrid.subtasks;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.data.Metadata;
/**
@ -12,13 +11,13 @@ import com.todoroo.astrid.data.Metadata;
*/
public class SubtasksMetadata {
static final int VALUE_UNSET = -1;
public static final long LIST_ACTIVE_TASKS = 0;
/** metadata key */
public static final String METADATA_KEY = "subtasks"; //$NON-NLS-1$
/** list id */
public static final StringProperty LIST_ID = new StringProperty(Metadata.TABLE,
public static final LongProperty LIST_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name);
public static final IntegerProperty INDENT = new IntegerProperty(Metadata.TABLE,

@ -0,0 +1,80 @@
package com.todoroo.astrid.subtasks;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
public class SubtasksUpdater extends OrderedListUpdater<Long> {
private static final String METADATA_ID = "mdi"; //$NON-NLS-1$
@Autowired MetadataService metadataService;
@Autowired TaskService taskService;
@Override
protected IntegerProperty indentProperty() {
return SubtasksMetadata.INDENT;
}
@Override
protected LongProperty orderProperty() {
return SubtasksMetadata.ORDER;
}
@Override
protected LongProperty parentProperty() {
return null;
}
@Override
protected Metadata getTaskMetadata(Long list, long taskId) {
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(Metadata.PROPERTIES).where(
Criterion.and(
Metadata.TASK.eq(taskId),
Metadata.KEY.eq(SubtasksMetadata.METADATA_KEY),
SubtasksMetadata.LIST_ID.eq(list))));
try {
cursor.moveToFirst();
if(cursor.isAfterLast())
return null;
return new Metadata(cursor);
} finally {
cursor.close();
}
}
@Override
protected Metadata createEmptyMetadata(Long list, long taskId) {
Metadata m = new Metadata();
m.setValue(Metadata.TASK, taskId);
m.setValue(Metadata.KEY, SubtasksMetadata.METADATA_KEY);
m.setValue(SubtasksMetadata.LIST_ID, list);
return m;
}
@Override
protected void iterateThroughList(Filter filter, Long list, OrderedListIterator iterator) {
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.ID,
Metadata.ID.as(METADATA_ID), Metadata.TASK, Metadata.KEY, SubtasksMetadata.INDENT,
SubtasksMetadata.ORDER).withQueryTemplate(filter.sqlQuery));
TodorooCursor<Metadata> metadataCursor = new TodorooCursor<Metadata>(cursor.getCursor(),
cursor.getProperties());
Metadata metadata = new Metadata();
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
metadata.readFromCursor(metadataCursor);
metadata.setId(cursor.getLong(cursor.getColumnIndex(METADATA_ID)));
iterator.processTask(cursor.get(Task.ID), metadata);
}
} finally {
cursor.close();
}
}
}
Loading…
Cancel
Save