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) {
      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<>();
    final int parentIndent = item.getValue(updater.indentProperty());
    updater.applyToChildren(
        list,
        itemId,
        new OrderedListNodeVisitor() {
          @Override
          public void visitNode(Node node) {
            Task childTask = taskService.fetchById(node.taskId, Task.RECURRENCE);
            if (!TextUtils.isEmpty(childTask.getValue(Task.RECURRENCE))) {
              Metadata metadata = updater.getTaskMetadata(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();
    }
  }
 @Override
 public void onClick(View v) {
   if (v == null) {
     return;
   }
   taskAdapter.getListener().onClick(v);
 }
        @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(list, targetTaskId, -1);
            } else {
              updater.moveTo(list, targetTaskId, destinationTaskId);
            }
          } catch (Exception e) {
            Log.e("drag", "Drag Error", e); // $NON-NLS-1$ //$NON-NLS-2$
          }

          fragment.loadTaskListContent(true);
        }
 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(list, targetTaskId, delta);
   } catch (Exception e) {
     Log.e("drag", "Indent Error", e); // $NON-NLS-1$ //$NON-NLS-2$
   }
   fragment.loadTaskListContent(true);
 }
  @Override
  public TaskAdapter createTaskAdapter(
      TodorooCursor<Task> cursor, AtomicReference<String> sqlQueryTemplate) {

    taskAdapter =
        new DraggableTaskAdapter(
            fragment, TaskListFragment.getTaskRowResource(), cursor, sqlQueryTemplate);

    taskAdapter.addOnCompletedTaskListener(
        new OnCompletedTaskListener() {
          @Override
          public void onCompletedTask(Task item, boolean newState) {
            setCompletedForItemAndSubtasks(item, newState);
          }
        });

    return taskAdapter;
  }
 @Override
 public void onDeleteTask(Task task) {
   updater.onDeleteTask(list, task.getId());
   taskAdapter.notifyDataSetInvalidated();
 }