Add ViewHolderFactory

pull/493/head
Alex Baker 8 years ago
parent 7a8af4e46f
commit d63424e0de

@ -16,20 +16,13 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.GtasksListFragment;
import org.tasks.tasklist.TagFormatter;
import org.tasks.themes.Theme;
import org.tasks.ui.CheckBoxes;
import java.util.ArrayList;
import java.util.Arrays;
@ -45,18 +38,10 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
return fragment;
}
@Inject TaskDao taskDao;
@Inject MetadataDao metadataDao;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject Preferences preferences;
@Inject DialogBuilder dialogBuilder;
@Inject CheckBoxes checkBoxes;
@Inject @ForApplication Context context;
@Inject Theme theme;
@Inject TagFormatter tagFormatter;
@Inject OrderedMetadataListFragmentHelper helper;
protected OrderedMetadataListFragmentHelper helper;
private int lastVisibleIndex = -1;
@Override
@ -85,8 +70,7 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
helper = new OrderedMetadataListFragmentHelper(preferences, taskAttachmentDao, taskDao,
metadataDao, this, gtasksTaskListUpdater, dialogBuilder, checkBoxes, tagFormatter);
helper.setTaskListFragment(this);
}
@Override
@ -125,11 +109,6 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
unregisterForContextMenu(listView);
}
@Override
public void onTaskCreated(String uuid) {
helper.onCreateTask(uuid);
}
@Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate());

@ -20,7 +20,6 @@ import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
@ -30,12 +29,8 @@ import com.todoroo.astrid.ui.DraggableListView.GrabberClickListener;
import com.todoroo.astrid.ui.DraggableListView.SwipeListener;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.ManualSortHelper;
import org.tasks.tasklist.TagFormatter;
import org.tasks.tasklist.ViewHolder;
import org.tasks.ui.CheckBoxes;
import org.tasks.tasklist.ViewHolderFactory;
import java.util.ArrayList;
import java.util.Collections;
@ -43,39 +38,34 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import timber.log.Timber;
class OrderedMetadataListFragmentHelper {
private final DisplayMetrics metrics = new DisplayMetrics();
private final GtasksTaskListUpdater updater;
private final DialogBuilder dialogBuilder;
private final CheckBoxes checkBoxes;
private final TagFormatter tagFormatter;
private final TaskListFragment fragment;
private final ViewHolderFactory viewHolderFactory;
private final Preferences preferences;
private final TaskAttachmentDao taskAttachmentDao;
private final TaskDao taskDao;
private final MetadataDao metadataDao;
private DraggableTaskAdapter taskAdapter;
private TaskListFragment fragment;
private GtasksList list;
OrderedMetadataListFragmentHelper(Preferences preferences, TaskAttachmentDao taskAttachmentDao,
TaskDao taskDao, MetadataDao metadataDao,
TaskListFragment fragment, GtasksTaskListUpdater updater,
DialogBuilder dialogBuilder, CheckBoxes checkBoxes, TagFormatter tagFormatter) {
this.preferences = preferences;
this.taskAttachmentDao = taskAttachmentDao;
@Inject
OrderedMetadataListFragmentHelper(TaskDao taskDao, MetadataDao metadataDao,
GtasksTaskListUpdater updater, ViewHolderFactory viewHolderFactory) {
this.taskDao = taskDao;
this.metadataDao = metadataDao;
this.fragment = fragment;
this.updater = updater;
this.dialogBuilder = dialogBuilder;
this.checkBoxes = checkBoxes;
this.tagFormatter = tagFormatter;
this.viewHolderFactory = viewHolderFactory;
}
void setTaskListFragment(TaskListFragment fragment) {
this.fragment = fragment;
}
// --- ui component setup
@ -99,7 +89,7 @@ class OrderedMetadataListFragmentHelper {
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
public void beforeSetUpTaskList(Filter filter) {
void beforeSetUpTaskList(Filter filter) {
updater.initialize(filter);
}
@ -175,32 +165,22 @@ class OrderedMetadataListFragmentHelper {
TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor,
AtomicReference<String> sqlQueryTemplate) {
taskAdapter = new DraggableTaskAdapter(context, preferences, fragment, cursor,
sqlQueryTemplate, dialogBuilder, checkBoxes, tagFormatter);
taskAdapter = new DraggableTaskAdapter(context, fragment, cursor, sqlQueryTemplate);
taskAdapter.addOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
return taskAdapter;
}
private final class DraggableTaskAdapter extends TaskAdapter {
private final ManualSortHelper manualSortHelper;
private DraggableTaskAdapter(Context context, Preferences preferences, TaskListFragment activity,
Cursor c, AtomicReference<String> query, DialogBuilder dialogBuilder,
CheckBoxes checkBoxes, TagFormatter tagFormatter) {
super(context, preferences, taskAttachmentDao, taskDao, activity, c, query,
dialogBuilder, checkBoxes, tagFormatter);
manualSortHelper = new ManualSortHelper(context);
private DraggableTaskAdapter(Context context, TaskListFragment activity,
Cursor c, AtomicReference<String> query) {
super(context, taskDao, activity, c, query, viewHolderFactory);
}
@Override
public synchronized void setFieldContentsAndVisibility(View view) {
super.setFieldContentsAndVisibility(view);
ViewHolder vh = (ViewHolder) view.getTag();
vh.setMinimumHeight(manualSortHelper.getMinRowHeight());
protected void adjustView(ViewHolder vh) {
int indent = vh.task.getValue(GtasksMetadata.INDENT);
vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
}
@ -257,10 +237,6 @@ class OrderedMetadataListFragmentHelper {
this.list = list;
}
public void onCreateTask(String uuid) {
//
}
void onDeleteTask(Task task) {
updater.onDeleteTask(list, task.getId());
taskAdapter.notifyDataSetInvalidated();

@ -39,7 +39,6 @@ import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.CustomFilter;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.BuiltInFilterExposer;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
@ -62,10 +61,9 @@ import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent;
import org.tasks.injection.InjectingListFragment;
import org.tasks.notifications.NotificationManager;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.TagFormatter;
import org.tasks.tasklist.ViewHolder;
import org.tasks.tasklist.ViewHolderFactory;
import org.tasks.ui.CheckBoxes;
import org.tasks.ui.MenuColorizer;
@ -114,8 +112,6 @@ public class TaskListFragment extends InjectingListFragment implements
@Inject TaskDuplicator taskDuplicator;
@Inject @ForActivity Context context;
@Inject Preferences preferences;
@Inject NotificationManager notificationManager;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject DialogBuilder dialogBuilder;
@Inject CheckBoxes checkBoxes;
@ -125,7 +121,7 @@ public class TaskListFragment extends InjectingListFragment implements
@Inject protected TaskListDataProvider taskListDataProvider;
@Inject TimerPlugin timerPlugin;
@Inject TaskDao taskDao;
@Inject TagFormatter tagFormatter;
@Inject ViewHolderFactory viewHolderFactory;
@BindView(R.id.swipe_layout) SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.swipe_layout_empty) SwipeRefreshLayout emptyView;
@ -471,8 +467,8 @@ public class TaskListFragment extends InjectingListFragment implements
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return new TaskAdapter(context, preferences, taskAttachmentDao, taskDao, this, cursor,
taskListDataProvider.getSqlQueryTemplate(), dialogBuilder, checkBoxes, tagFormatter);
return new TaskAdapter(context, taskDao, this, cursor,
taskListDataProvider.getSqlQueryTemplate(), viewHolderFactory);
}
public static final String TAGS_METADATA_JOIN = "for_tags"; //$NON-NLS-1$

@ -5,26 +5,14 @@
*/
package com.todoroo.astrid.adapter;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Paint;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.common.collect.Lists;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
@ -33,38 +21,20 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Pair;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.core.LinkActionExposer;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.files.FilesAction;
import com.todoroo.astrid.notes.NotesAction;
import com.todoroo.astrid.tags.TaskToTagMetadata;
import com.todoroo.astrid.ui.CheckableImageView;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.TagFormatter;
import org.tasks.tasklist.ViewHolder;
import org.tasks.ui.CheckBoxes;
import org.tasks.tasklist.ViewHolderFactory;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import timber.log.Timber;
import static android.support.v4.content.ContextCompat.getColor;
import static com.google.common.collect.Lists.newArrayList;
import static org.tasks.preferences.ResourceResolver.getData;
/**
* Adapter for displaying a user's tasks as a list
*
@ -105,58 +75,24 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
// --- instance variables
private final CheckBoxes checkBoxes;
private final Preferences preferences;
private final TaskAttachmentDao taskAttachmentDao;
private final TaskDao taskDao;
private final Context context;
private final TaskListFragment fragment;
private final DialogBuilder dialogBuilder;
private final Resources resources;
private OnCompletedTaskListener onCompletedTaskListener = null;
private final LayoutInflater inflater;
private int fontSize;
private final AtomicReference<String> query;
private final int textColorSecondary;
private final int textColorHint;
private final int textColorOverdue;
private final TagFormatter tagFormatter;
private final ViewHolderFactory viewHolderFactory;
public TaskAdapter(Context context, Preferences preferences, TaskAttachmentDao taskAttachmentDao,
TaskDao taskDao, TaskListFragment fragment, Cursor c,
AtomicReference<String> query, DialogBuilder dialogBuilder,
CheckBoxes checkBoxes, TagFormatter tagFormatter) {
public TaskAdapter(Context context, TaskDao taskDao, TaskListFragment fragment, Cursor c,
AtomicReference<String> query, ViewHolderFactory viewHolderFactory) {
super(context, c, false);
this.checkBoxes = checkBoxes;
this.preferences = preferences;
this.taskAttachmentDao = taskAttachmentDao;
this.taskDao = taskDao;
this.context = context;
this.query = query;
this.fragment = fragment;
this.dialogBuilder = dialogBuilder;
this.resources = fragment.getResources();
inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
fontSize = preferences.getIntegerFromString(R.string.p_fontSize, 18);
textColorSecondary = getData(context, android.R.attr.textColorSecondary);
textColorHint = getData(context, android.R.attr.textColorTertiary);
textColorOverdue = getColor(context, R.color.overdue);
this.tagFormatter = tagFormatter;
tagFormatter.updateTagMap();
this.viewHolderFactory = viewHolderFactory;
}
/* ======================================================================
* =========================================================== filterable
* ====================================================================== */
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (getFilterQueryProvider() != null) {
@ -202,34 +138,28 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
return taskDao.query(Query.select(properties).withQueryTemplate(sql));
}
/* ======================================================================
* =========================================================== view setup
* ====================================================================== */
/** Creates a new view for use in the list view */
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewGroup view = (ViewGroup)inflater.inflate(R.layout.task_adapter_row_simple, parent, false);
boolean showFullTaskTitle = preferences.getBoolean(R.string.p_fullTaskTitle, false);
ViewGroup view = (ViewGroup) LayoutInflater.from(context)
.inflate(R.layout.task_adapter_row_simple, parent, false);
// create view holder
new ViewHolder(view, showFullTaskTitle, fontSize);
// add UI component listeners
addListeners(view);
viewHolderFactory.newViewHolder(view, this::onTaskCompleted);
return view;
}
/** Populates a view with content */
@Override
public void bindView(View view, Context context, Cursor c) {
TodorooCursor<Task> cursor = (TodorooCursor<Task>)c;
ViewHolder viewHolder = ((ViewHolder)view.getTag());
viewHolder.bindView(cursor);
setFieldContentsAndVisibility(view);
setTaskAppearance(viewHolder, viewHolder.task);
adjustView(viewHolder);
}
protected void adjustView(ViewHolder viewHolder) {
}
public String getItemUuid(int position) {
@ -245,48 +175,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
}
}
/** Helper method to set the contents and visibility of each field */
public synchronized void setFieldContentsAndVisibility(View view) {
ViewHolder viewHolder = (ViewHolder)view.getTag();
Task task = viewHolder.task;
// name
final TextView nameView = viewHolder.nameView;
String nameValue = task.getTitle();
long hiddenUntil = task.getHideUntil();
if(task.getDeletionDate() > 0) {
nameValue = resources.getString(R.string.TAd_deletedFormat, nameValue);
}
if(hiddenUntil > DateUtilities.now()) {
nameValue = resources.getString(R.string.TAd_hiddenFormat, nameValue);
}
nameView.setText(nameValue);
setupDueDateAndTags(viewHolder, task);
// Task action
ImageView taskAction = viewHolder.taskActionIcon;
if (taskAction != null) {
TaskAction action = getTaskAction(task, viewHolder.hasFiles, viewHolder.hasNotes);
if (action != null) {
viewHolder.taskActionContainer.setVisibility(View.VISIBLE);
taskAction.setImageResource(action.icon);
taskAction.setTag(action);
} else {
viewHolder.taskActionContainer.setVisibility(View.GONE);
taskAction.setTag(null);
}
}
}
private TaskAction getTaskAction(Task task, boolean hasFiles, boolean hasNotes) {
if (task.isCompleted()) {
return null;
}
return LinkActionExposer.getActionsForTask(context, task, hasFiles, hasNotes);
}
public void onClick(View v) {
// expand view (unless deleted)
final ViewHolder viewHolder = (ViewHolder)v.getTag();
@ -298,67 +186,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
fragment.onTaskListItemClicked(taskId);
}
private Pair<Float, Float> lastTouchYRawY = new Pair<>(0f, 0f);
/**
* Set listeners for this view. This is called once per view when it is
* created.
*/
private void addListeners(final View container) {
final ViewHolder viewHolder = (ViewHolder)container.getTag();
// check box listener
OnTouchListener otl = (v, event) -> {
lastTouchYRawY = new Pair<>(event.getY(), event.getRawY());
return false;
};
viewHolder.completeBox.setOnTouchListener(otl);
viewHolder.completeBox.setOnClickListener(completeBoxListener);
if (viewHolder.taskActionContainer != null) {
viewHolder.taskActionContainer.setOnClickListener(v -> {
TaskAction action = (TaskAction) viewHolder.taskActionIcon.getTag();
if (action instanceof NotesAction) {
showEditNotesDialog(viewHolder.task);
} else if (action instanceof FilesAction) {
showFilesDialog(viewHolder.task);
} else if (action != null) {
try {
action.intent.send();
} catch (CanceledException e) {
// Oh well
Timber.e(e, e.getMessage());
}
}
});
}
}
private void showEditNotesDialog(final Task task) {
Task t = taskDao.fetch(task.getId(), Task.NOTES);
if (t == null || !t.hasNotes()) {
return;
}
SpannableString description = new SpannableString(t.getNotes());
Linkify.addLinks(description, Linkify.ALL);
AlertDialog dialog = dialogBuilder.newDialog()
.setMessage(description)
.setPositiveButton(android.R.string.ok, null)
.show();
View message = dialog.findViewById(android.R.id.message);
if (message != null && message instanceof TextView) {
((TextView) message).setMovementMethod(LinkMovementMethod.getInstance());
}
}
private void showFilesDialog(Task task) {
// TODO: reimplement this
// FilesControlSet filesControlSet = new FilesControlSet();
// filesControlSet.hideAddAttachmentButton();
// filesControlSet.readFromTask(task);
// filesControlSet.getView().performClick();
}
/* ======================================================================
* ======================================================= event handlers
* ====================================================================== */
@ -366,147 +193,16 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
tagFormatter.updateTagMap();
viewHolderFactory.updateTagMap();
}
private final View.OnClickListener completeBoxListener = v -> {
int[] location = new int[2];
v.getLocationOnScreen(location);
ViewHolder viewHolder = getTagFromCheckBox(v);
if(Math.abs(location[1] + lastTouchYRawY.getLeft() - lastTouchYRawY.getRight()) > 10) {
viewHolder.completeBox.setChecked(!viewHolder.completeBox.isChecked());
return;
}
Task task = viewHolder.task;
completeTask(task, viewHolder.completeBox.isChecked());
// set check box to actual action item state
setTaskAppearance(viewHolder, task);
};
private ViewHolder getTagFromCheckBox(View v) {
return (ViewHolder)((View)v.getParent()).getTag();
}
/** Helper method to adjust a tasks' appearance if the task is completed or
* uncompleted.
*/
private void setTaskAppearance(ViewHolder viewHolder, Task task) {
boolean completed = task.isCompleted();
TextView name = viewHolder.nameView;
if (completed) {
name.setEnabled(false);
name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
name.setEnabled(true);
name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
}
name.setTextSize(fontSize);
setupDueDateAndTags(viewHolder, task);
float detailTextSize = Math.max(10, fontSize * 14 / 20);
if(viewHolder.dueDate != null) {
viewHolder.dueDate.setTextSize(detailTextSize);
viewHolder.dueDate.setTypeface(null, 0);
}
setupCompleteBox(viewHolder);
}
private void setupCompleteBox(ViewHolder viewHolder) {
// complete box
final Task task = viewHolder.task;
final CheckableImageView checkBoxView = viewHolder.completeBox;
boolean completed = task.isCompleted();
checkBoxView.setChecked(completed);
if (completed) {
checkBoxView.setImageDrawable(checkBoxes.getCompletedCheckbox(task.getImportance()));
} else if (TextUtils.isEmpty(task.getRecurrence())) {
checkBoxView.setImageDrawable(checkBoxes.getCheckBox(task.getImportance()));
} else {
checkBoxView.setImageDrawable(checkBoxes.getRepeatingCheckBox(task.getImportance()));
}
checkBoxView.invalidate();
}
private void setupDueDateAndTags(ViewHolder viewHolder, Task task) {
// due date / completion date
final TextView dueDateView = viewHolder.dueDate;
if(!task.isCompleted() && task.hasDueDate()) {
long dueDate = task.getDueDate();
if(task.isOverdue()) {
dueDateView.setTextColor(textColorOverdue);
} else {
dueDateView.setTextColor(textColorSecondary);
}
String dateValue = DateUtilities.getRelativeDateStringWithTime(context, dueDate);
dueDateView.setText(dateValue);
dueDateView.setVisibility(View.VISIBLE);
} else if(task.isCompleted()) {
String dateValue = DateUtilities.getRelativeDateStringWithTime(context, task.getCompletionDate());
dueDateView.setText(resources.getString(R.string.TAd_completed, dateValue));
dueDateView.setTextColor(textColorHint);
dueDateView.setVisibility(View.VISIBLE);
} else {
dueDateView.setVisibility(View.GONE);
}
if (task.isCompleted()) {
viewHolder.tagBlock.setVisibility(View.GONE);
} else {
String tags = viewHolder.tagsString;
List<String> tagUuids = tags != null ? newArrayList(tags.split(",")) : Lists.newArrayList();
CharSequence tagString = tagFormatter.getTagString(tagUuids);
if (TextUtils.isEmpty(tagString)) {
viewHolder.tagBlock.setVisibility(View.GONE);
} else {
viewHolder.tagBlock.setText(tagString);
viewHolder.tagBlock.setVisibility(View.VISIBLE);
}
}
}
/**
* This method is called when user completes a task via check box or other
* means
*
* @param newState
* state that this task should be set to
*/
private void completeTask(final Task task, final boolean newState) {
if(task == null) {
return;
}
if (newState != task.isCompleted()) {
if(onCompletedTaskListener != null) {
onCompletedTaskListener.onCompletedTask(task, newState);
}
taskDao.setComplete(task, newState);
private void onTaskCompleted(Task task, boolean newState) {
if (onCompletedTaskListener != null) {
onCompletedTaskListener.onCompletedTask(task, newState);
}
}
/**
* Add a new listener
*/
public void addOnCompletedTaskListener(final OnCompletedTaskListener newListener) {
if(this.onCompletedTaskListener == null) {
this.onCompletedTaskListener = newListener;
} else {
final OnCompletedTaskListener old = this.onCompletedTaskListener;
this.onCompletedTaskListener = (item, newState) -> {
old.onCompletedTask(item, newState);
newListener.onCompletedTask(item, newState);
};
}
public void setOnCompletedTaskListener(final OnCompletedTaskListener newListener) {
this.onCompletedTaskListener = newListener;
}
}

@ -17,7 +17,6 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
@ -28,12 +27,8 @@ import com.todoroo.astrid.ui.DraggableListView.GrabberClickListener;
import com.todoroo.astrid.ui.DraggableListView.SwipeListener;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.ManualSortHelper;
import org.tasks.tasklist.TagFormatter;
import org.tasks.tasklist.ViewHolder;
import org.tasks.ui.CheckBoxes;
import org.tasks.tasklist.ViewHolderFactory;
import java.util.ArrayList;
import java.util.Collections;
@ -41,36 +36,30 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import timber.log.Timber;
class AstridOrderedListFragmentHelper {
private final DisplayMetrics metrics = new DisplayMetrics();
private final SubtasksFilterUpdater updater;
private final DialogBuilder dialogBuilder;
private final CheckBoxes checkBoxes;
private final TaskListFragment fragment;
private final Preferences preferences;
private final TaskAttachmentDao taskAttachmentDao;
private final TagFormatter tagFormatter;
private final TaskDao taskDao;
private final ViewHolderFactory viewHolderFactory;
private DraggableTaskAdapter taskAdapter;
private TaskListFragment fragment;
private TaskListMetadata list;
AstridOrderedListFragmentHelper(Preferences preferences, TaskAttachmentDao taskAttachmentDao,
TaskListFragment fragment, SubtasksFilterUpdater updater,
DialogBuilder dialogBuilder, CheckBoxes checkBoxes,
TagFormatter tagFormatter, TaskDao taskDao) {
this.preferences = preferences;
this.taskAttachmentDao = taskAttachmentDao;
this.fragment = fragment;
@Inject
AstridOrderedListFragmentHelper(SubtasksFilterUpdater updater, TaskDao taskDao, ViewHolderFactory viewHolderFactory) {
this.updater = updater;
this.dialogBuilder = dialogBuilder;
this.checkBoxes = checkBoxes;
this.tagFormatter = tagFormatter;
this.taskDao = taskDao;
this.viewHolderFactory = viewHolderFactory;
}
void setTaskListFragment(TaskListFragment fragment) {
this.fragment = fragment;
}
// --- ui component setup
@ -177,33 +166,22 @@ class AstridOrderedListFragmentHelper {
TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor,
AtomicReference<String> sqlQueryTemplate) {
taskAdapter = new DraggableTaskAdapter(context, preferences, fragment, cursor,
sqlQueryTemplate, dialogBuilder, checkBoxes, tagFormatter);
taskAdapter = new DraggableTaskAdapter(context, fragment, cursor, sqlQueryTemplate);
taskAdapter.addOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
return taskAdapter;
}
private final class DraggableTaskAdapter extends TaskAdapter {
private final ManualSortHelper manualSortHelper;
private DraggableTaskAdapter(Context context, Preferences preferences, TaskListFragment activity,
Cursor c, AtomicReference<String> query, DialogBuilder dialogBuilder,
CheckBoxes checkBoxes, TagFormatter tagFormatter) {
super(context, preferences, taskAttachmentDao, taskDao, activity, c, query,
dialogBuilder, checkBoxes, tagFormatter);
manualSortHelper = new ManualSortHelper(context);
private DraggableTaskAdapter(Context context, TaskListFragment activity, Cursor c,
AtomicReference<String> query) {
super(context, taskDao, activity, c, query, viewHolderFactory);
}
@Override
public synchronized void setFieldContentsAndVisibility(View view) {
super.setFieldContentsAndVisibility(view);
ViewHolder vh = (ViewHolder) view.getTag();
vh.setMinimumHeight(manualSortHelper.getMinRowHeight());
protected void adjustView(ViewHolder vh) {
int indent = updater.getIndentForTask(vh.task.getUuid());
vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
}

@ -17,20 +17,16 @@ import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.BuiltInFilterExposer;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskListMetadataDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.TagFormatter;
import org.tasks.themes.Theme;
import org.tasks.ui.CheckBoxes;
import javax.inject.Inject;
@ -48,27 +44,20 @@ public class SubtasksListFragment extends TaskListFragment {
return fragment;
}
protected AstridOrderedListFragmentHelper helper;
private int lastVisibleIndex = -1;
@Inject SubtasksFilterUpdater subtasksFilterUpdater;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject Preferences preferences;
@Inject @ForApplication Context context;
@Inject DialogBuilder dialogBuilder;
@Inject TaskListMetadataDao taskListMetadataDao;
@Inject CheckBoxes checkBoxes;
@Inject Theme theme;
@Inject TaskDao taskDao;
@Inject TagFormatter tagFormatter;
@Inject AstridOrderedListFragmentHelper helper;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
helper = new AstridOrderedListFragmentHelper(preferences, taskAttachmentDao,
this, subtasksFilterUpdater, dialogBuilder, checkBoxes, tagFormatter, taskDao);
helper.setTaskListFragment(this);
}
@Override

@ -14,8 +14,6 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.TagFilter;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskListMetadataDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData;
@ -23,14 +21,10 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent;
import org.tasks.preferences.Preferences;
import org.tasks.tasklist.TagFormatter;
import org.tasks.tasklist.TagListFragment;
import org.tasks.themes.Theme;
import org.tasks.ui.CheckBoxes;
import javax.inject.Inject;
@ -43,18 +37,10 @@ public class SubtasksTagListFragment extends TagListFragment {
return fragment;
}
@Inject SubtasksFilterUpdater subtasksFilterUpdater;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject Preferences preferences;
@Inject @ForApplication Context context;
@Inject DialogBuilder dialogBuilder;
@Inject TaskListMetadataDao taskListMetadataDao;
@Inject CheckBoxes checkBoxes;
@Inject Theme theme;
@Inject TaskDao taskDao;
@Inject TagFormatter tagFormatter;
private AstridOrderedListFragmentHelper helper;
@Inject AstridOrderedListFragmentHelper helper;
private int lastVisibleIndex = -1;
@ -62,8 +48,7 @@ public class SubtasksTagListFragment extends TagListFragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
helper = new AstridOrderedListFragmentHelper(preferences, taskAttachmentDao,
this, subtasksFilterUpdater, dialogBuilder, checkBoxes, tagFormatter, taskDao);
helper.setTaskListFragment(this);
}
@Override

@ -1,30 +0,0 @@
package org.tasks.tasklist;
import android.content.Context;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import org.tasks.injection.ForApplication;
import javax.inject.Inject;
public class ManualSortHelper {
private final Resources resources;
private final int minRowHeight;
@Inject
public ManualSortHelper(@ForApplication Context context) {
resources = context.getResources();
minRowHeight = computeMinRowHeight();
}
private int computeMinRowHeight() {
DisplayMetrics metrics = resources.getDisplayMetrics();
return (int) (metrics.density * 40);
}
public int getMinRowHeight() {
return minRowHeight;
}
}

@ -1,19 +1,42 @@
package org.tasks.tasklist;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Paint;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.common.collect.Lists;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Pair;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.core.LinkActionExposer;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.files.FilesAction;
import com.todoroo.astrid.notes.NotesAction;
import com.todoroo.astrid.ui.CheckableImageView;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.ui.CheckBoxes;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import timber.log.Timber;
import static com.google.common.collect.Lists.newArrayList;
/**
* View Holder saves a lot of findViewById lookups.
@ -21,22 +44,50 @@ import butterknife.ButterKnife;
* @author Tim Su <tim@todoroo.com>
*/
public class ViewHolder {
public interface OnCompletedTaskCallback {
void onCompletedTask(Task task, boolean newState);
}
@BindView(R.id.rowBody) public ViewGroup rowBody;
@BindView(R.id.title) public TextView nameView;
@BindView(R.id.title) TextView nameView;
@BindView(R.id.completeBox) public CheckableImageView completeBox;
@BindView(R.id.due_date) public TextView dueDate;
@BindView(R.id.tag_block) public TextView tagBlock;
@BindView(R.id.tag_block) TextView tagBlock;
@BindView(R.id.taskActionContainer) public View taskActionContainer;
@BindView(R.id.taskActionIcon) public ImageView taskActionIcon;
public Task task;
public String tagsString; // From join query, not part of the task model
public boolean hasFiles; // From join query, not part of the task model
public boolean hasNotes;
private String tagsString; // From join query, not part of the task model
private boolean hasFiles; // From join query, not part of the task model
private boolean hasNotes;
private final Context context;
private final int fontSize;
private final CheckBoxes checkBoxes;
private final TagFormatter tagFormatter;
private final int textColorSecondary;
private final int textColorHint;
private final TaskDao taskDao;
private final DialogBuilder dialogBuilder;
private final OnCompletedTaskCallback callback;
private final int textColorOverdue;
private Pair<Float, Float> lastTouchYRawY = new Pair<>(0f, 0f);
public ViewHolder(ViewGroup view, boolean showFullTaskTitle, int fontSize) {
public ViewHolder(Context context, ViewGroup view, boolean showFullTaskTitle, int fontSize,
CheckBoxes checkBoxes, TagFormatter tagFormatter,
int textColorOverdue, int textColorSecondary, int textColorHint, TaskDao taskDao,
DialogBuilder dialogBuilder, OnCompletedTaskCallback callback, int minRowHeight) {
this.context = context;
this.fontSize = fontSize;
this.checkBoxes = checkBoxes;
this.tagFormatter = tagFormatter;
this.textColorOverdue = textColorOverdue;
this.textColorSecondary = textColorSecondary;
this.textColorHint = textColorHint;
this.taskDao = taskDao;
this.dialogBuilder = dialogBuilder;
this.callback = callback;
ButterKnife.bind(this, view);
task = new Task();
@ -51,6 +102,10 @@ public class ViewHolder {
for(int i = 0; i < view.getChildCount(); i++) {
view.getChildAt(i).setTag(this);
}
setMinimumHeight(minRowHeight);
addListeners();
}
public void bindView(TodorooCursor<Task> cursor) {
@ -60,6 +115,9 @@ public class ViewHolder {
// TODO: see if this is a performance issue
task = new Task(cursor);
setFieldContentsAndVisibility();
setTaskAppearance();
}
public void setMinimumHeight(int minRowHeight) {
@ -71,4 +129,209 @@ public class ViewHolder {
completeBox.setMinimumHeight(minRowHeight);
}
}
/** Helper method to set the contents and visibility of each field */
private synchronized void setFieldContentsAndVisibility() {
String nameValue = task.getTitle();
long hiddenUntil = task.getHideUntil();
if(task.getDeletionDate() > 0) {
nameValue = context.getResources().getString(R.string.TAd_deletedFormat, nameValue);
}
if(hiddenUntil > DateUtilities.now()) {
nameValue = context.getResources().getString(R.string.TAd_hiddenFormat, nameValue);
}
nameView.setText(nameValue);
setupDueDateAndTags();
// Task action
ImageView taskAction = taskActionIcon;
if (taskAction != null) {
TaskAction action = getTaskAction(task, hasFiles, hasNotes);
if (action != null) {
taskActionContainer.setVisibility(View.VISIBLE);
taskAction.setImageResource(action.icon);
taskAction.setTag(action);
} else {
taskActionContainer.setVisibility(View.GONE);
taskAction.setTag(null);
}
}
}
private TaskAction getTaskAction(Task task, boolean hasFiles, boolean hasNotes) {
if (task.isCompleted()) {
return null;
}
return LinkActionExposer.getActionsForTask(context, task, hasFiles, hasNotes);
}
public void setTaskAppearance() {
boolean completed = task.isCompleted();
TextView name = nameView;
if (completed) {
name.setEnabled(false);
name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
name.setEnabled(true);
name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
}
name.setTextSize(fontSize);
setupDueDateAndTags();
float detailTextSize = Math.max(10, fontSize * 14 / 20);
if(dueDate != null) {
dueDate.setTextSize(detailTextSize);
dueDate.setTypeface(null, 0);
}
setupCompleteBox();
}
private void setupCompleteBox() {
// complete box
final CheckableImageView checkBoxView = completeBox;
boolean completed = task.isCompleted();
checkBoxView.setChecked(completed);
if (completed) {
checkBoxView.setImageDrawable(checkBoxes.getCompletedCheckbox(task.getImportance()));
} else if (TextUtils.isEmpty(task.getRecurrence())) {
checkBoxView.setImageDrawable(checkBoxes.getCheckBox(task.getImportance()));
} else {
checkBoxView.setImageDrawable(checkBoxes.getRepeatingCheckBox(task.getImportance()));
}
checkBoxView.invalidate();
}
private void setupDueDateAndTags() {
// due date / completion date
final TextView dueDateView = dueDate;
if(!task.isCompleted() && task.hasDueDate()) {
long dueDate = task.getDueDate();
if(task.isOverdue()) {
dueDateView.setTextColor(textColorOverdue);
} else {
dueDateView.setTextColor(textColorSecondary);
}
String dateValue = DateUtilities.getRelativeDateStringWithTime(context, dueDate);
dueDateView.setText(dateValue);
dueDateView.setVisibility(View.VISIBLE);
} else if(task.isCompleted()) {
String dateValue = DateUtilities.getRelativeDateStringWithTime(context, task.getCompletionDate());
dueDateView.setText(context.getResources().getString(R.string.TAd_completed, dateValue));
dueDateView.setTextColor(textColorHint);
dueDateView.setVisibility(View.VISIBLE);
} else {
dueDateView.setVisibility(View.GONE);
}
if (task.isCompleted()) {
tagBlock.setVisibility(View.GONE);
} else {
String tags = tagsString;
List<String> tagUuids = tags != null ? newArrayList(tags.split(",")) : Lists.newArrayList();
CharSequence tagString = tagFormatter.getTagString(tagUuids);
if (TextUtils.isEmpty(tagString)) {
tagBlock.setVisibility(View.GONE);
} else {
tagBlock.setText(tagString);
tagBlock.setVisibility(View.VISIBLE);
}
}
}
/**
* Set listeners for this view. This is called once per view when it is
* created.
*/
private void addListeners() {
// check box listener
View.OnTouchListener otl = (v, event) -> {
lastTouchYRawY = new Pair<>(event.getY(), event.getRawY());
return false;
};
completeBox.setOnTouchListener(otl);
completeBox.setOnClickListener(completeBoxListener);
if (taskActionContainer != null) {
taskActionContainer.setOnClickListener(v -> {
TaskAction action = (TaskAction) taskActionIcon.getTag();
if (action instanceof NotesAction) {
showEditNotesDialog(task);
} else if (action instanceof FilesAction) {
showFilesDialog(task);
} else if (action != null) {
try {
action.intent.send();
} catch (PendingIntent.CanceledException e) {
// Oh well
Timber.e(e, e.getMessage());
}
}
});
}
}
private void showEditNotesDialog(final Task task) {
Task t = taskDao.fetch(task.getId(), Task.NOTES);
if (t == null || !t.hasNotes()) {
return;
}
SpannableString description = new SpannableString(t.getNotes());
Linkify.addLinks(description, Linkify.ALL);
AlertDialog dialog = dialogBuilder.newDialog()
.setMessage(description)
.setPositiveButton(android.R.string.ok, null)
.show();
View message = dialog.findViewById(android.R.id.message);
if (message != null && message instanceof TextView) {
((TextView) message).setMovementMethod(LinkMovementMethod.getInstance());
}
}
private void showFilesDialog(Task task) {
// TODO: reimplement this
// FilesControlSet filesControlSet = new FilesControlSet();
// filesControlSet.hideAddAttachmentButton();
// filesControlSet.readFromTask(task);
// filesControlSet.getView().performClick();
}
private final View.OnClickListener completeBoxListener = v -> {
int[] location = new int[2];
v.getLocationOnScreen(location);
if(Math.abs(location[1] + lastTouchYRawY.getLeft() - lastTouchYRawY.getRight()) > 10) {
completeBox.setChecked(!completeBox.isChecked());
return;
}
completeTask(task, completeBox.isChecked());
// set check box to actual action item state
setTaskAppearance();
};
/**
* This method is called when user completes a task via check box or other
* means
*
* @param newState
* state that this task should be set to
*/
private void completeTask(final Task task, final boolean newState) {
if(task == null) {
return;
}
if (newState != task.isCompleted()) {
callback.onCompletedTask(task, newState);
taskDao.setComplete(task, newState);
}
}
}

@ -0,0 +1,62 @@
package org.tasks.tasklist;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.ViewGroup;
import com.todoroo.astrid.dao.TaskDao;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.Preferences;
import org.tasks.ui.CheckBoxes;
import javax.inject.Inject;
import static android.support.v4.content.ContextCompat.getColor;
import static org.tasks.preferences.ResourceResolver.getData;
public class ViewHolderFactory {
private final int textColorSecondary;
private final int textColorHint;
private final int textColorOverdue;
private final Context context;
private final CheckBoxes checkBoxes;
private final TagFormatter tagFormatter;
private final boolean showFullTaskTitle;
private final int fontSize;
private final TaskDao taskDao;
private final DialogBuilder dialogBuilder;
private final int minRowHeight;
@Inject
public ViewHolderFactory(@ForActivity Context context, Preferences preferences,
CheckBoxes checkBoxes, TagFormatter tagFormatter, TaskDao taskDao,
DialogBuilder dialogBuilder) {
this.context = context;
this.checkBoxes = checkBoxes;
this.tagFormatter = tagFormatter;
this.taskDao = taskDao;
this.dialogBuilder = dialogBuilder;
textColorSecondary = getData(context, android.R.attr.textColorSecondary);
textColorHint = getData(context, android.R.attr.textColorTertiary);
textColorOverdue = getColor(context, R.color.overdue);
showFullTaskTitle = preferences.getBoolean(R.string.p_fullTaskTitle, false);
fontSize = preferences.getIntegerFromString(R.string.p_fontSize, 18);
tagFormatter.updateTagMap();
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
minRowHeight = (int) (metrics.density * 40);
}
public ViewHolder newViewHolder(ViewGroup viewGroup, ViewHolder.OnCompletedTaskCallback onCompletedTaskCallback) {
return new ViewHolder(context, viewGroup, showFullTaskTitle, fontSize, checkBoxes,
tagFormatter, textColorOverdue, textColorSecondary, textColorHint, taskDao,
dialogBuilder, onCompletedTaskCallback, minRowHeight);
}
public void updateTagMap() {
tagFormatter.updateTagMap();
}
}
Loading…
Cancel
Save