Convert subtasks to use uuids instead of ids

pull/14/head
Sam Bosley 12 years ago
parent af33bf37c2
commit 841c5b178f

@ -230,55 +230,6 @@ public final class ActFmSyncService {
}
}
public void fetchFilterOrder(String localFilterId) {
if (!checkForToken())
return;
String filterId = SubtasksHelper.serverFilterOrderId(localFilterId);
ArrayList<Object> params = new ArrayList<Object>();
params.add("filter"); params.add(filterId);
params.add("token"); params.add(token);
try {
JSONObject result = actFmInvoker.invoke("list_order", params.toArray(new Object[params.size()]));
String order = result.optString("order");
if (!TextUtils.isEmpty(order) && !"null".equals(order))
Preferences.setString(localFilterId, SubtasksHelper.convertTreeToLocalIds(order));
} catch (IOException e) {
handleException("fetch-filter-order", e);
}
}
public void fetchTagOrder(TagData tagData) {
if (!checkForToken())
return;
if (!RemoteModel.isValidUuid(tagData.getUuid()))
return;
try {
JSONObject result = actFmInvoker.invoke("list_order", "tag_id", tagData.getValue(TagData.UUID), "token", token);
JSONArray ordering = result.optJSONArray("order");
if (ordering == null)
return;
if (ordering.optLong(0) != -1L) {
JSONArray newOrdering = new JSONArray();
newOrdering.put(-1L);
for (int i = 0; i < ordering.length(); i++)
newOrdering.put(ordering.get(i));
ordering = newOrdering;
}
String orderString = ordering.toString();
String localOrder = SubtasksHelper.convertTreeToLocalIds(orderString);
tagData.setValue(TagData.TAG_ORDERING, localOrder);
tagDataService.save(tagData);
} catch (JSONException e) {
handleException("fetch-tag-order-json", e);
} catch (IOException e) {
handleException("fetch-tag-order-io", e);
}
}
// --- data fetch methods
public int fetchFeaturedLists(int serverTime) throws JSONException, IOException {
if (!checkForToken())

@ -46,6 +46,7 @@ import com.todoroo.astrid.files.FileMetadata;
import com.todoroo.astrid.helper.UUIDHelper;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.subtasks.SubtasksUpdater;
import com.todoroo.astrid.tags.TaskToTagMetadata;
@ -310,6 +311,8 @@ public class AstridNewSyncMigrator {
if (TextUtils.isEmpty(activeTasksOrder))
activeTasksOrder = "[]";
activeTasksOrder = SubtasksHelper.convertTreeToRemoteIds(activeTasksOrder);
TaskListMetadata tlm = new TaskListMetadata();
tlm.setValue(TaskListMetadata.FILTER, TaskListMetadata.FILTER_ID_ALL);
tlm.setValue(TaskListMetadata.TASK_IDS, activeTasksOrder);
@ -321,6 +324,8 @@ public class AstridNewSyncMigrator {
if (TextUtils.isEmpty(todayTasksOrder))
todayTasksOrder = "[]";
todayTasksOrder = SubtasksHelper.convertTreeToRemoteIds(todayTasksOrder);
tlm.setValue(TaskListMetadata.FILTER, TaskListMetadata.FILTER_ID_TODAY);
tlm.setValue(TaskListMetadata.TASK_IDS, todayTasksOrder);
tlm.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
@ -335,6 +340,8 @@ public class AstridNewSyncMigrator {
td.readFromCursor(allTagData);
String tagOrdering = td.getValue(TagData.TAG_ORDERING);
tagOrdering = SubtasksHelper.convertTreeToRemoteIds(tagOrdering);
tlm.setValue(TaskListMetadata.TASK_IDS, tagOrdering);
tlm.setValue(TaskListMetadata.TAG_UUID, td.getUuid());
if (!tagsThatNeedOrderingSync.contains(td.getId()))

@ -232,22 +232,21 @@ public class AstridOrderedListFragmentHelper<LIST> implements OrderedListFragmen
}
}
private final Map<Long, ArrayList<Long>> chainedCompletions =
Collections.synchronizedMap(new HashMap<Long, ArrayList<Long>>());
private final Map<String, ArrayList<String>> chainedCompletions =
Collections.synchronizedMap(new HashMap<String, ArrayList<String>>());
private void setCompletedForItemAndSubtasks(final Task item, final boolean completedState) {
final long itemId = item.getId();
final String itemId = item.getUuid();
final Task model = new Task();
final long completionDate = completedState ? DateUtilities.now() : 0;
if(completedState == false) {
ArrayList<Long> chained = chainedCompletions.get(itemId);
ArrayList<String> chained = chainedCompletions.get(itemId);
if(chained != null) {
for(Long taskId : chained) {
model.setId(taskId);
for(String taskId : chained) {
model.setValue(Task.COMPLETION_DATE, completionDate);
taskService.save(model);
taskService.update(Task.UUID.eq(taskId), model);
model.clear();
taskAdapter.getCompletedItems().put(taskId, false);
@ -257,23 +256,23 @@ public class AstridOrderedListFragmentHelper<LIST> implements OrderedListFragmen
return;
}
final ArrayList<Long> chained = new ArrayList<Long>();
final ArrayList<String> chained = new ArrayList<String>();
updater.applyToDescendants(itemId, new AstridOrderedListUpdater.OrderedListNodeVisitor() {
@Override
public void visitNode(AstridOrderedListUpdater.Node node) {
model.setId(node.taskId);
String uuid = node.uuid;
model.setValue(Task.COMPLETION_DATE, completionDate);
taskService.save(model);
taskService.update(Task.UUID.eq(uuid), model);
model.clear();
taskAdapter.getCompletedItems().put(node.taskId, true);
chained.add(node.taskId);
taskAdapter.getCompletedItems().put(node.uuid, true);
chained.add(node.uuid);
}
});
if(chained.size() > 0) {
// move recurring items to item parent
TodorooCursor<Task> recurring = taskService.query(Query.select(Task.ID, Task.RECURRENCE).where(
TodorooCursor<Task> recurring = taskService.query(Query.select(Task.ID, Task.UUID, Task.RECURRENCE).where(
Criterion.and(Task.ID.in(chained.toArray(new Long[chained.size()])),
Task.RECURRENCE.isNotNull(), Functions.length(Task.RECURRENCE).gt(0))));
try {
@ -283,7 +282,7 @@ public class AstridOrderedListFragmentHelper<LIST> implements OrderedListFragmen
t.clear();
t.readFromCursor(recurring);
if (!TextUtils.isEmpty(t.getValue(Task.RECURRENCE))) {
updater.moveToParentOf(t.getId(), itemId);
updater.moveToParentOf(t.getUuid(), itemId);
madeChanges = true;
}
}
@ -311,14 +310,14 @@ public class AstridOrderedListFragmentHelper<LIST> implements OrderedListFragmen
}
public void onCreateTask(Task task) {
updater.onCreateTask(list, getFilter(), task.getId());
updater.onCreateTask(list, getFilter(), task.getUuid());
fragment.reconstructCursor();
fragment.loadTaskListContent(true);
fragment.selectCustomId(task.getId());
}
public void onDeleteTask(Task task) {
updater.onDeleteTask(list, getFilter(), task.getId());
updater.onDeleteTask(list, getFilter(), task.getUuid());
taskAdapter.notifyDataSetInvalidated();
}

@ -26,7 +26,7 @@ public abstract class AstridOrderedListUpdater<LIST> {
public AstridOrderedListUpdater() {
DependencyInjectionService.getInstance().inject(this);
idToNode = new HashMap<Long, Node>();
idToNode = new HashMap<String, Node>();
}
public interface OrderedListNodeVisitor {
@ -34,15 +34,16 @@ public abstract class AstridOrderedListUpdater<LIST> {
}
public static class Node {
public long taskId;
// public long taskId;
public String uuid; // For parsing and syncing -- not used elsewhere
public Node parent;
public int indent;
public final ArrayList<Node> children = new ArrayList<Node>();
public Node(long taskId, Node parent, int indent) {
this.taskId = taskId;
this.uuid = "-1"; //$NON-NLS-1$
public Node(String uuid, Node parent, int indent) {
// this.taskId = taskId;
this.uuid = uuid;
// this.uuid = "-1"; //$NON-NLS-1$
this.parent = parent;
this.indent = indent;
}
@ -50,7 +51,7 @@ public abstract class AstridOrderedListUpdater<LIST> {
private Node treeRoot;
private final HashMap<Long, Node> idToNode;
private final HashMap<String, Node> idToNode;
protected abstract String getSerializedTree(LIST list, Filter filter);
protected abstract void writeSerialization(LIST list, String serialized, boolean shouldQueueSync);
@ -71,7 +72,7 @@ public abstract class AstridOrderedListUpdater<LIST> {
treeRoot = buildTreeModel(serializedTree, new JSONTreeModelBuilder() {
@Override
public void afterAddNode(Node node) {
idToNode.put(node.taskId, node);
idToNode.put(node.uuid, node);
}
});
verifyTreeModel(list, filter);
@ -79,16 +80,16 @@ public abstract class AstridOrderedListUpdater<LIST> {
private void verifyTreeModel(LIST list, Filter filter) {
boolean changedThings = false;
Set<Long> keySet = idToNode.keySet();
Set<Long> currentIds = new HashSet<Long>();
for (Long id : keySet) {
Set<String> keySet = idToNode.keySet();
Set<String> currentIds = new HashSet<String>();
for (String id : keySet) {
currentIds.add(id);
}
Set<Long> idsInQuery = new HashSet<Long>();
TodorooCursor<Task> tasks = taskService.fetchFiltered(filter.getSqlQuery(), null, Task.ID);
Set<String> idsInQuery = new HashSet<String>();
TodorooCursor<Task> tasks = taskService.fetchFiltered(filter.getSqlQuery(), null, Task.UUID);
try {
for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) {
Long id = tasks.getLong(0);
String id = tasks.getString(0);
idsInQuery.add(id);
if (idToNode.containsKey(id))
continue;
@ -111,8 +112,8 @@ public abstract class AstridOrderedListUpdater<LIST> {
writeSerialization(list, serializeTree(), false);
}
private void removeNodes(Set<Long> idsToRemove) {
for (Long id : idsToRemove) {
private void removeNodes(Set<String> idsToRemove) {
for (String id : idsToRemove) {
Node node = idToNode.get(id);
if (node == null)
continue;
@ -132,39 +133,39 @@ public abstract class AstridOrderedListUpdater<LIST> {
return idToNode.get(taskId);
}
public Long[] getOrderedIds() {
ArrayList<Long> ids = new ArrayList<Long>();
private String[] getOrderedIds() {
ArrayList<String> ids = new ArrayList<String>();
orderedIdHelper(treeRoot, ids);
return ids.toArray(new Long[ids.size()]);
return ids.toArray(new String[ids.size()]);
}
public String getOrderString() {
Long[] ids = getOrderedIds();
String[] ids = getOrderedIds();
return buildOrderString(ids);
}
public static String buildOrderString(Long[] 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.ID.eq(ids[i]).toString());
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<Long> ids) {
private void orderedIdHelper(Node node, List<String> ids) {
if (node != treeRoot)
ids.add(node.taskId);
ids.add(node.uuid);
for (Node child : node.children) {
orderedIdHelper(child, ids);
}
}
public void applyToDescendants(long taskId, OrderedListNodeVisitor visitor) {
public void applyToDescendants(String taskId, OrderedListNodeVisitor visitor) {
Node n = idToNode.get(taskId);
if (n == null)
return;
@ -262,7 +263,7 @@ public abstract class AstridOrderedListUpdater<LIST> {
moveHelper(list, filter, target, before);
}
public void moveToParentOf(long moveThis, long toParentOfThis) {
public void moveToParentOf(String moveThis, String toParentOfThis) {
Node target = idToNode.get(toParentOfThis);
if (target == null)
return;
@ -328,18 +329,18 @@ public abstract class AstridOrderedListUpdater<LIST> {
applyToFilter(filter);
}
public void onCreateTask(LIST list, Filter filter, long taskId) {
if (idToNode.containsKey(taskId) || taskId < 0)
public void onCreateTask(LIST list, Filter filter, String uuid) {
if (idToNode.containsKey(uuid) || !RemoteModel.isValidUuid(uuid))
return;
Node newNode = new Node(taskId, treeRoot, 0);
Node newNode = new Node(uuid, treeRoot, 0);
treeRoot.children.add(newNode);
idToNode.put(taskId, newNode);
idToNode.put(uuid, newNode);
writeSerialization(list, serializeTree(), true);
applyToFilter(filter);
}
public void onDeleteTask(LIST list, Filter filter, long taskId) {
public void onDeleteTask(LIST list, Filter filter, String taskId) {
Node task = idToNode.get(taskId);
if (task == null)
return;
@ -367,41 +368,28 @@ public abstract class AstridOrderedListUpdater<LIST> {
}
public static Node buildTreeModel(String serializedTree, JSONTreeModelBuilder callback) {
return buildTreeModel(serializedTree, callback, false);
}
public static Node buildTreeModel(String serializedTree, JSONTreeModelBuilder callback, boolean useUuid) {
Node root = new Node(-1, null, -1);
Node root = new Node("-1", null, -1); //$NON-NLS-1$
try {
JSONArray tree = new JSONArray(serializedTree);
recursivelyBuildChildren(root, tree, callback, useUuid);
recursivelyBuildChildren(root, tree, callback);
} catch (JSONException e) {
Log.e("OrderedListUpdater", "Error building tree model", e); //$NON-NLS-1$//$NON-NLS-2$
}
return root;
}
private static void recursivelyBuildChildren(Node node, JSONArray children, JSONTreeModelBuilder callback, boolean useUuid) throws JSONException {
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);
Long id = 0L;
String uuid = RemoteModel.NO_UUID;
if (!useUuid) {
if (subarray == null)
id = children.getLong(i);
else
id = subarray.getLong(0);
} else {
if (subarray == null)
uuid = children.getString(i);
else
uuid = subarray.getString(0);
}
if (subarray == null)
uuid = children.getString(i);
else
uuid = subarray.getString(0);
Node child = new Node(id, node, node.indent + 1);
if (useUuid)
child.uuid = uuid;
Node child = new Node(uuid, node, node.indent + 1);
if (subarray != null)
recursivelyBuildChildren(child, subarray, callback, useUuid);
recursivelyBuildChildren(child, subarray, callback);
node.children.add(child);
if (callback != null)
callback.afterAddNode(child);
@ -409,43 +397,33 @@ public abstract class AstridOrderedListUpdater<LIST> {
}
protected String serializeTree() {
return serializeTree(treeRoot, false);
return serializeTree(treeRoot);
}
public static String serializeTree(Node root) {
return serializeTree(root, false);
}
public static String serializeTree(Node root, boolean useUuid) {
JSONArray tree = new JSONArray();
if (root == null) {
return tree.toString();
}
try {
recursivelySerialize(root, tree, useUuid);
recursivelySerialize(root, tree);
} catch (JSONException e) {
Log.e("OrderedListUpdater", "Error serializing tree model", e); //$NON-NLS-1$//$NON-NLS-2$
}
return tree.toString();
}
private static void recursivelySerialize(Node node, JSONArray serializeTo, boolean useUuid) throws JSONException {
private static void recursivelySerialize(Node node, JSONArray serializeTo) throws JSONException {
ArrayList<Node> children = node.children;
if (useUuid)
serializeTo.put(node.uuid);
else
serializeTo.put(node.taskId);
serializeTo.put(node.uuid);
for (Node child : children) {
if (child.children.size() > 0) {
JSONArray branch = new JSONArray();
recursivelySerialize(child, branch, useUuid);
recursivelySerialize(child, branch);
serializeTo.put(branch);
} else {
if (useUuid)
serializeTo.put(child.uuid);
else
serializeTo.put(child.taskId);
serializeTo.put(child.uuid);
}
}
}

@ -91,11 +91,12 @@ public class SubtasksHelper {
else
serialized = Preferences.getStringValue(SubtasksUpdater.ACTIVE_TASKS_ORDER);
return AstridOrderedListUpdater.buildOrderString(getIdArray(serialized));
return AstridOrderedListUpdater.buildOrderString(getStringIdArray(serialized));
}
@SuppressWarnings("nls")
public static Long[] getIdArray(String serializedTree) {
@Deprecated
private static Long[] getIdArray(String serializedTree) {
ArrayList<Long> ids = new ArrayList<Long>();
String[] digitsOnly = serializedTree.split("[\\[\\],\\s]"); // Split on [ ] , or whitespace chars
for (String idString : digitsOnly) {
@ -132,14 +133,19 @@ public class SubtasksHelper {
Node tree = AstridOrderedListUpdater.buildTreeModel(localTree, null);
remapLocalTreeToRemote(tree, idMap);
return AstridOrderedListUpdater.serializeTree(tree, true);
return AstridOrderedListUpdater.serializeTree(tree);
}
private static void remapLocalTreeToRemote(Node root, HashMap<Long, String> idMap) {
ArrayList<Node> children = root.children;
for (int i = 0; i < children.size(); i++) {
Node child = children.get(i);
String uuid = idMap.get(child.taskId);
long localId = -1L;
try {
localId = Long.parseLong(child.uuid);
} catch (NumberFormatException e) {/**/}
String uuid = idMap.get(localId);
if (!RemoteModel.isValidUuid(uuid)) {
children.remove(i);
children.addAll(i, child.children);
@ -151,37 +157,6 @@ public class SubtasksHelper {
}
}
/**
* Takes a subtasks string containing UUIDs and remaps it to one containing local ids
* @param remoteTree
* @return
*/
public static String convertTreeToLocalIds(String remoteTree) {
String[] uuids = getStringIdArray(remoteTree);
HashMap<String, Long> idMap = getIdMap(uuids, Task.UUID, Task.ID);
idMap.put("-1", -1L); //$NON-NLS-1$
Node tree = AstridOrderedListUpdater.buildTreeModel(remoteTree, null, true);
remapRemoteTreeToLocal(tree, idMap);
return AstridOrderedListUpdater.serializeTree(tree);
}
private static void remapRemoteTreeToLocal(Node root, HashMap<String, Long> idMap) {
ArrayList<Node> children = root.children;
for (int i = 0; i < children.size(); i++) {
Node child = children.get(i);
Long localId = idMap.get(child.uuid);
if (localId == null || localId <= 0) {
children.remove(i);
children.addAll(i, child.children);
i--;
} else {
child.taskId = localId;
remapRemoteTreeToLocal(child, idMap);
}
}
}
private static <A, B> HashMap<A, B> getIdMap(A[] keys, Property<A> keyProperty, Property<B> valueProperty) {
HashMap<A, B> map = new HashMap<A, B>();
TodorooCursor<Task> tasks = PluginServices.getTaskService().query(Query.select(keyProperty, valueProperty).where(keyProperty.in(keys)));

@ -93,7 +93,7 @@ public class SubtasksMetadataMigration {
private String buildTreeModelFromMetadata(String tag, TodorooCursor<Metadata> cursor) {
Metadata item = new Metadata();
Node root = new Node(-1, null, -1);
Node root = new Node("-1", null, -1); //$NON-NLS-1$
for (; !cursor.isAfterLast(); cursor.moveToNext()) {
item.clear();
item.readFromCursor(cursor);
@ -107,10 +107,10 @@ public class SubtasksMetadataMigration {
indent = i.intValue();
}
Node parent = findNextParentForIndent(root, indent);
Node newNode = new Node(item.getValue(Metadata.TASK), parent, parent.indent + 1);
Node newNode = new Node(item.getValue(Metadata.TASK).toString(), parent, parent.indent + 1);
parent.children.add(newNode);
}
return AstridOrderedListUpdater.serializeTree(root, false);
return AstridOrderedListUpdater.serializeTree(root);
}
private Node findNextParentForIndent(Node root, int indent) {

@ -216,7 +216,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
protected final Context context;
protected final TaskListFragment fragment;
protected final Resources resources;
protected final HashMap<Long, Boolean> completedItems = new HashMap<Long, Boolean>(0);
protected final HashMap<Object, Boolean> completedItems = new HashMap<Object, Boolean>(0);
protected OnCompletedTaskListener onCompletedTaskListener = null;
public boolean isFling = false;
protected final int resource;
@ -884,17 +884,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
startDetailThread();
}
/**
* Called to tell the cache to be cleared
*/
public void flushSpecific(long taskId) {
completedItems.put(taskId, null);
decorationManager.clearCache(taskId);
taskDetailLoader.remove(taskId);
taskActionLoader.remove(taskId);
}
public HashMap<Long, Boolean> getCompletedItems() {
public HashMap<Object, Boolean> getCompletedItems() {
return completedItems;
}
@ -1075,7 +1065,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if (activity == null)
return;
// show item as completed if it was recently checked
if(completedItems.get(task.getId()) != null) {
if(completedItems.get(task.getUuid()) != null || completedItems.get(task.getId()) != null) {
task.setValue(Task.COMPLETION_DATE,
completedItems.get(task.getId()) ? DateUtilities.now() : 0);
}
@ -1252,7 +1242,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if(onCompletedTaskListener != null)
onCompletedTaskListener.onCompletedTask(task, newState);
completedItems.put(task.getId(), newState);
completedItems.put(task.getUuid(), newState);
taskService.setComplete(task, newState);
}
}

Loading…
Cancel
Save