Make all subtasks fragments compile using the new helper classes (untested)

pull/14/head
Sam Bosley 12 years ago
parent 029e5450ab
commit 13d85be8e8

@ -27,7 +27,7 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.subtasks.OrderedListFragmentHelper;
import com.todoroo.astrid.subtasks.NewOrderedListFragmentHelper;
import com.todoroo.astrid.subtasks.SubtasksListFragment;
public class GtasksListFragment extends SubtasksListFragment {
@ -60,8 +60,8 @@ public class GtasksListFragment extends SubtasksListFragment {
};
@Override
protected OrderedListFragmentHelper<?> createFragmentHelper() {
return new OrderedListFragmentHelper<StoreObject>(this, gtasksTaskListUpdater);
protected NewOrderedListFragmentHelper<?> createFragmentHelper() {
return new NewOrderedListFragmentHelper<StoreObject>(this, null /*gtasksTaskListUpdater*/);
}
@Override
@ -75,7 +75,7 @@ public class GtasksListFragment extends SubtasksListFragment {
long storeObjectId = extras.getLong(TOKEN_STORE_ID, 0);
list = storeObjectDao.fetch(storeObjectId, LIST_PROPERTIES);
((OrderedListFragmentHelper<StoreObject>)helper).setList(list);
((NewOrderedListFragmentHelper<StoreObject>)helper).setList(list);
}
@Override

@ -0,0 +1,321 @@
package com.todoroo.astrid.subtasks;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ListView;
import com.commonsware.cwac.tlv.TouchListView.DropListener;
import com.commonsware.cwac.tlv.TouchListView.GrabberClickListener;
import com.commonsware.cwac.tlv.TouchListView.SwipeListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.ui.DraggableListView;
import com.todoroo.astrid.utility.AstridPreferences;
public class NewOrderedListFragmentHelper<LIST> {
private final DisplayMetrics metrics = new DisplayMetrics();
private final NewOrderedListUpdater<LIST> updater;
private final TaskListFragment fragment;
@Autowired TaskService taskService;
@Autowired MetadataService metadataService;
private DraggableTaskAdapter taskAdapter;
private LIST list;
public NewOrderedListFragmentHelper(TaskListFragment fragment, NewOrderedListUpdater<LIST> updater) {
DependencyInjectionService.getInstance().inject(this);
this.fragment = fragment;
this.updater = updater;
}
// --- ui component setup
private Activity getActivity() {
return fragment.getActivity();
}
private ListView getListView() {
return fragment.getListView();
}
private Filter getFilter() {
return fragment.getFilter();
}
public DraggableListView getTouchListView() {
DraggableListView tlv = (DraggableListView) fragment.getListView();
return tlv;
}
public void setUpUiComponents() {
TypedValue tv = new TypedValue();
getActivity().getTheme().resolveAttribute(R.attr.asThemeTextColor, tv, false);
getTouchListView().setDragndropBackgroundColor(tv.data);
getTouchListView().setDropListener(dropListener);
getTouchListView().setClickListener(rowClickListener);
getTouchListView().setSwipeListener(swipeListener);
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
if(Preferences.getInt(AstridPreferences.P_SUBTASKS_HELP, 0) == 0)
showSubtasksHelp();
}
@SuppressWarnings("nls")
private void showSubtasksHelp() {
String body = String.format("<h3>%s</h3><img src='%s'>" +
"<br>%s<br><br><br><img src='%s'><br>%s",
getActivity().getString(R.string.subtasks_help_1),
"subtasks_vertical.png",
getActivity().getString(R.string.subtasks_help_2),
"subtasks_horizontal.png",
getActivity().getString(R.string.subtasks_help_3));
String color = ThemeService.getDialogTextColorString();
String html = String.format("<html><body style='text-align:center;color:%s'>%s</body></html>",
color, body);
DialogUtilities.htmlDialog(getActivity(), html, R.string.subtasks_help_title);
Preferences.setInt(AstridPreferences.P_SUBTASKS_HELP, 1);
}
public void beforeSetUpTaskList(Filter filter) {
updater.initialize(list, filter);
}
// public Property<?>[] taskProperties() {
// ArrayList<Property<?>> properties = new ArrayList<Property<?>>(Arrays.asList(TaskAdapter.PROPERTIES));
// properties.add(updater.indentProperty());
// properties.add(updater.orderProperty());
// return properties.toArray(new Property<?>[properties.size()]);
// }
private final DropListener dropListener = new DropListener() {
@Override
public void drop(int from, int to) {
long targetTaskId = taskAdapter.getItemId(from);
if (targetTaskId <= 0) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
long destinationTaskId = taskAdapter.getItemId(to);
try {
if(to >= getListView().getCount())
updater.moveTo(targetTaskId, -1);
else
updater.moveTo(targetTaskId, destinationTaskId);
} catch (Exception e) {
Log.e("drag", "Drag Error", e); //$NON-NLS-1$ //$NON-NLS-2$
}
fragment.loadTaskListContent(true);
}
};
private final SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
indent(which, 1);
}
@Override
public void swipeLeft(int which) {
indent(which, -1);
}
protected void indent(int which, int delta) {
long targetTaskId = taskAdapter.getItemId(which);
if (targetTaskId <= 0) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
try {
updater.indent(targetTaskId, delta);
} catch (Exception e) {
Log.e("drag", "Indent Error", e); //$NON-NLS-1$ //$NON-NLS-2$
}
fragment.loadTaskListContent(true);
}
};
private final GrabberClickListener rowClickListener = new GrabberClickListener() {
@Override
public void onLongClick(final View v) {
if(v == null)
return;
fragment.registerForContextMenu(getListView());
getListView().showContextMenuForChild(v);
fragment.unregisterForContextMenu(getListView());
}
@Override
public void onClick(View v) {
if(v == null)
return;
((DraggableTaskAdapter) taskAdapter).getListener().onClick(v);
}
};
public TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor,
AtomicReference<String> sqlQueryTemplate) {
int resource = Preferences.getBoolean(R.string.p_taskRowStyle, false) ?
R.layout.task_adapter_row_simple : R.layout.task_adapter_row;
taskAdapter = new DraggableTaskAdapter(fragment, resource,
cursor, sqlQueryTemplate, false, null);
taskAdapter.addOnCompletedTaskListener(new OnCompletedTaskListener() {
@Override
public void onCompletedTask(Task item, boolean newState) {
setCompletedForItemAndSubtasks(item, newState);
}
});
return taskAdapter;
}
private final class DraggableTaskAdapter extends TaskAdapter {
private DraggableTaskAdapter(TaskListFragment activity, int resource,
Cursor c, AtomicReference<String> query, boolean autoRequery,
OnCompletedTaskListener onCompletedTaskListener) {
super(activity, resource, c, query, autoRequery,
onCompletedTaskListener);
applyListeners = APPLY_LISTENERS_NONE;
}
@Override
protected ViewHolder getTagFromCheckBox(View v) {
return (ViewHolder)((View)v.getParent()).getTag();
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = super.newView(context, cursor, parent);
view.getLayoutParams().height = Math.round(45 * metrics.density);
ViewHolder vh = (ViewHolder) view.getTag();
MarginLayoutParams rowParams = (MarginLayoutParams) vh.rowBody.getLayoutParams();
rowParams.topMargin = rowParams.bottomMargin = 0;
ViewGroup.LayoutParams pictureParams = vh.picture.getLayoutParams();
pictureParams.width = pictureParams.height = Math.round(38 * metrics.density);
pictureParams = vh.pictureBorder.getLayoutParams();
pictureParams.width = pictureParams.height = Math.round(38 * metrics.density);
return view;
}
@Override
public synchronized void setFieldContentsAndVisibility(View view) {
super.setFieldContentsAndVisibility(view);
ViewHolder vh = (ViewHolder) view.getTag();
int indent = updater.getIndentForTask(vh.task.getId());
vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
}
@Override
protected void addListeners(View container) {
super.addListeners(container);
}
public TaskRowListener getListener() {
return listener;
}
}
private final Map<Long, ArrayList<Long>> chainedCompletions =
Collections.synchronizedMap(new HashMap<Long, ArrayList<Long>>());
private void setCompletedForItemAndSubtasks(final Task item, final boolean completedState) {
final long itemId = item.getId();
final Task model = new Task();
final long completionDate = completedState ? DateUtilities.now() : 0;
if(completedState == false) {
ArrayList<Long> chained = chainedCompletions.get(itemId);
if(chained != null) {
for(Long taskId : chained) {
model.setId(taskId);
model.setValue(Task.COMPLETION_DATE, completionDate);
taskService.save(model);
model.clear();
taskAdapter.getCompletedItems().put(taskId, false);
}
taskAdapter.notifyDataSetInvalidated();
}
return;
}
final ArrayList<Long> chained = new ArrayList<Long>();
// final int parentIndent = item.getValue(updater.indentProperty());
updater.applyToDescendants(itemId, new NewOrderedListUpdater.OrderedListNodeVisitor() {
@Override
public void visitNode(NewOrderedListUpdater.Node node) {
// Task childTask = taskService.fetchById(node.taskId, Task.RECURRENCE);
//
// if(!TextUtils.isEmpty(childTask.getValue(Task.RECURRENCE))) {
// Metadata metadata = updater.getTaskMetadata(list, node.taskId);
// metadata.setValue(updater.indentProperty(), parentIndent);
// metadataService.save(metadata);
// }
model.setId(node.taskId);
model.setValue(Task.COMPLETION_DATE, completionDate);
taskService.save(model);
model.clear();
taskAdapter.getCompletedItems().put(node.taskId, true);
chained.add(node.taskId);
}
});
if(chained.size() > 0) {
chainedCompletions.put(itemId, chained);
taskAdapter.notifyDataSetInvalidated();
}
}
public void setList(LIST list) {
this.list = list;
}
public void onDeleteTask(Task task) {
updater.onDeleteTask(task.getId());
taskAdapter.notifyDataSetInvalidated();
}
}

@ -12,6 +12,7 @@ import android.util.Log;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.data.Task;
public abstract class NewOrderedListUpdater<LIST> {
@ -20,8 +21,8 @@ public abstract class NewOrderedListUpdater<LIST> {
idToNode = new HashMap<Long, Node>();
}
public interface OrderedListIterator {
public void processTask(Node node);
public interface OrderedListNodeVisitor {
public void visitNode(Node node);
}
protected static class Node {
@ -41,7 +42,8 @@ public abstract class NewOrderedListUpdater<LIST> {
private final HashMap<Long, Node> idToNode;
protected abstract String getSerializedTree();
protected abstract String getSerializedTree(LIST list);
protected abstract void writeSerialization(LIST list, String serialized);
public int getIndentForTask(long targetTaskId) {
Node n = idToNode.get(targetTaskId);
@ -51,7 +53,7 @@ public abstract class NewOrderedListUpdater<LIST> {
}
protected void initialize(LIST list, Filter filter) {
treeRoot = buildTreeModel(getSerializedTree());
treeRoot = buildTreeModel(getSerializedTree(list));
}
public Long[] getOrderedIds() {
@ -60,6 +62,17 @@ public abstract class NewOrderedListUpdater<LIST> {
return ids.toArray(new Long[ids.size()]);
}
public String getOrderString() {
Long[] ids = getOrderedIds();
StringBuilder builder = new StringBuilder();
for (int i = ids.length - 1; i >= 0; i--) {
builder.append(Task.ID.eq(ids[i]).toString());
if (i > 0)
builder.append(", "); //$NON-NLS-1$
}
return builder.toString();
}
private void orderedIdHelper(Node node, List<Long> ids) {
if (node != treeRoot)
ids.add(node.taskId);
@ -69,6 +82,25 @@ public abstract class NewOrderedListUpdater<LIST> {
}
}
public void applyToDescendants(long taskId, OrderedListNodeVisitor visitor) {
Node n = idToNode.get(taskId);
if (n == null)
return;
applyToDescendantsHelper(n, visitor);
}
private void applyToDescendantsHelper(Node n, OrderedListNodeVisitor visitor) {
ArrayList<Node> children = n.children;
for (Node child : children) {
visitor.visitNode(child);
applyToDescendantsHelper(child, visitor);
}
}
public void iterateOverList(OrderedListNodeVisitor visitor) {
applyToDescendantsHelper(treeRoot, visitor);
}
public void indent(long targetTaskId, int delta) {
Node node = idToNode.get(targetTaskId);
indentHelper(node, delta);
@ -148,6 +180,20 @@ public abstract class NewOrderedListUpdater<LIST> {
moveThis.parent = treeRoot;
}
public void onDeleteTask(long taskId) {
Node task = idToNode.get(taskId);
if (task == null)
return;
Node parent = task.parent;
ArrayList<Node> siblings = parent.children;
siblings.remove(task);
for (Node child : task.children) {
indentHelper(child, -1);
}
idToNode.remove(taskId);
}
private Node buildTreeModel(String serializedTree) {
Node root = new Node(-1, null, -1);
try {

@ -10,10 +10,10 @@ import android.view.View;
import android.view.ViewGroup;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
/**
@ -24,7 +24,7 @@ import com.todoroo.astrid.data.Task;
*/
public class SubtasksListFragment extends TaskListFragment {
protected OrderedListFragmentHelper<?> helper;
protected NewOrderedListFragmentHelper<?> helper;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@ -32,10 +32,10 @@ public class SubtasksListFragment extends TaskListFragment {
super.onActivityCreated(savedInstanceState);
}
protected OrderedListFragmentHelper<?> createFragmentHelper() {
OrderedListFragmentHelper<String> olfh =
new OrderedListFragmentHelper<String>(this, new SubtasksUpdater());
olfh.setList(SubtasksMetadata.LIST_ACTIVE_TASKS);
protected NewOrderedListFragmentHelper<?> createFragmentHelper() {
NewOrderedListFragmentHelper<TagData> olfh =
new NewOrderedListFragmentHelper<TagData>(this, new SubtasksUpdater());
olfh.setList(getActiveTagData());
return olfh;
}
@ -60,12 +60,6 @@ public class SubtasksListFragment extends TaskListFragment {
unregisterForContextMenu(getListView());
}
@Override
public Property<?>[] taskProperties() {
return helper.taskProperties();
}
@Override
protected boolean isDraggable() {
return true;

@ -9,25 +9,24 @@ import android.view.View;
import android.view.ViewGroup;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.actfm.TagViewFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
public class SubtasksTagListFragment extends TagViewFragment {
private final OrderedListFragmentHelper<String> helper;
private final NewOrderedListFragmentHelper<TagData> helper;
public SubtasksTagListFragment() {
super();
helper = new OrderedListFragmentHelper<String>(this, new SubtasksUpdater());
helper = new NewOrderedListFragmentHelper<TagData>(this, new SubtasksUpdater());
}
@Override
protected void postLoadTagData() {
String list = "td:" + tagData.getId(); //$NON-NLS-1$
helper.setList(list);
helper.setList(tagData);
}
@Override
@ -58,12 +57,6 @@ public class SubtasksTagListFragment extends TagViewFragment {
unregisterForContextMenu(getListView());
}
@Override
public Property<?>[] taskProperties() {
return helper.taskProperties();
}
@Override
protected boolean isDraggable() {
return true;

@ -5,154 +5,64 @@
*/
package com.todoroo.astrid.subtasks;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
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.andlib.utility.Preferences;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.TaskService;
public class SubtasksUpdater extends OrderedListUpdater<String> {
private static final String METADATA_ID = "mdi"; //$NON-NLS-1$
public class SubtasksUpdater extends NewOrderedListUpdater<TagData> {
@Autowired MetadataService metadataService;
@Autowired TagDataService tagDataService;
@Autowired TaskService taskService;
@Override
protected IntegerProperty indentProperty() {
return SubtasksMetadata.INDENT;
}
public static final String ACTIVE_TASKS_ORDER = "active_tasks_order"; //$NON-NLS-1$
@Override
protected LongProperty orderProperty() {
return SubtasksMetadata.ORDER;
}
@Override
protected LongProperty parentProperty() {
return null;
protected void initialize(TagData list, Filter filter) {
super.initialize(list, filter);
applySubtasksToFilter(filter);
}
@Override
protected void initialize(String list, Filter filter) {
applySubtasksToFilter(filter, list);
sanitizeTaskList(filter, list);
}
@SuppressWarnings("nls")
public void applySubtasksToFilter(Filter filter) {
String query = filter.getSqlQuery();
@Override
protected Metadata getTaskMetadata(String 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.TAG.eq(list))));
try {
cursor.moveToFirst();
if(cursor.isAfterLast())
return null;
return new Metadata(cursor);
} finally {
cursor.close();
}
}
query = query.replaceAll("ORDER BY .*", "");
query = query + String.format(" ORDER BY %s, %s, %s, %s)",
Task.DELETION_DATE, Task.COMPLETION_DATE,
getOrderString(), Task.CREATION_DATE);
query = query.replace(TaskCriteria.isVisible().toString(),
Criterion.all.toString());
@Override
protected Metadata createEmptyMetadata(String list, long taskId) {
Metadata m = new Metadata();
m.setValue(Metadata.TASK, taskId);
m.setValue(Metadata.KEY, SubtasksMetadata.METADATA_KEY);
m.setValue(SubtasksMetadata.TAG, list);
return m;
filter.setFilterQueryOverride(query);
}
@Override
protected void iterateThroughList(Filter filter, String 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.getSqlQuery()));
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();
protected String getSerializedTree(TagData list) {
if (list == null) {
String order = Preferences.getStringValue(ACTIVE_TASKS_ORDER);
if (order == null)
order = "{}"; //$NON-NLS-1$
return order;
}
return list.getValue(TagData.NAME);
}
@SuppressWarnings("nls")
public void applySubtasksToFilter(Filter filter, String tagName) {
String query = filter.getSqlQuery();
if(tagName == null)
tagName = SubtasksMetadata.LIST_ACTIVE_TASKS;
String subtaskJoin = String.format("LEFT JOIN %s ON (%s = %s AND %s = '%s' AND %s = '%s') ",
Metadata.TABLE, Task.ID, Metadata.TASK,
Metadata.KEY, SubtasksMetadata.METADATA_KEY,
SubtasksMetadata.TAG, tagName);
if(!query.contains(subtaskJoin)) {
query = subtaskJoin + query;
query = query.replaceAll("ORDER BY .*", "");
query = query + String.format(" ORDER BY %s, %s, IFNULL(CAST(%s AS LONG), %s)",
Task.DELETION_DATE, Task.COMPLETION_DATE,
SubtasksMetadata.ORDER, Task.CREATION_DATE);
query = query.replace(TaskCriteria.isVisible().toString(),
Criterion.all.toString());
filter.setFilterQueryOverride(query);
@Override
protected void writeSerialization(TagData list, String serialized) {
if (list == null)
Preferences.setString(ACTIVE_TASKS_ORDER, serialized);
else {
list.setValue(TagData.NAME, serialized);
tagDataService.save(list);
}
}
public void sanitizeTaskList(Filter filter, String list) {
final AtomicInteger previousIndent = new AtomicInteger(-1);
final AtomicLong previousOrder = new AtomicLong(-1);
final HashSet<Long> taskIds = new HashSet<Long>();
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if(!metadata.isSaved())
return;
if(taskIds.contains(taskId)) {
metadataService.delete(metadata);
return;
}
long order = metadata.getValue(SubtasksMetadata.ORDER);
if(order <= previousOrder.get()) // bad
order = previousOrder.get() + 1;
int indent = metadata.getValue(SubtasksMetadata.INDENT);
if(indent < 0 || indent > previousIndent.get() + 1) // bad
indent = Math.max(0, previousIndent.get() + 1);
metadata.setValue(SubtasksMetadata.ORDER, order);
metadata.setValue(SubtasksMetadata.INDENT, indent);
saveAndUpdateModifiedDate(metadata);
previousIndent.set(indent);
previousOrder.set(order);
taskIds.add(taskId);
}
});
}
}

Loading…
Cancel
Save