Drag and drop recycler view for task list

pull/493/head
Alex Baker 9 years ago
parent d63424e0de
commit 3a5c61d680

@ -8,8 +8,6 @@ package com.todoroo.astrid.gtasks;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
@ -18,7 +16,6 @@ import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.GtasksFilter; import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import org.tasks.R;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.tasklist.GtasksListFragment; import org.tasks.tasklist.GtasksListFragment;
@ -42,8 +39,6 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
@Inject Theme theme; @Inject Theme theme;
@Inject OrderedMetadataListFragmentHelper helper; @Inject OrderedMetadataListFragmentHelper helper;
private int lastVisibleIndex = -1;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -73,18 +68,6 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
helper.setTaskListFragment(this); helper.setTaskListFragment(this);
} }
@Override
protected int getListBody() {
return R.layout.task_list_body_subtasks;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
helper.setUpUiComponents();
}
@Override @Override
public void setTaskAdapter() { public void setTaskAdapter() {
helper.setList(list); helper.setList(list);
@ -93,25 +76,9 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
super.setTaskAdapter(); super.setTaskAdapter();
} }
@Override
public void onPause() {
super.onPause();
lastVisibleIndex = getListView().getFirstVisiblePosition();
}
@Override
public void onResume() {
super.onResume();
ListView listView = getListView();
if (lastVisibleIndex >= 0) {
listView.setSelection(lastVisibleIndex);
}
unregisterForContextMenu(listView);
}
@Override @Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) { protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate()); return helper.createTaskAdapter(theme.wrap(context), cursor);
} }
@Override @Override

@ -5,14 +5,9 @@
*/ */
package com.todoroo.astrid.gtasks; package com.todoroo.astrid.gtasks;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.ListView;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
@ -23,20 +18,11 @@ import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.ui.DraggableListView;
import com.todoroo.astrid.ui.DraggableListView.DropListener;
import com.todoroo.astrid.ui.DraggableListView.GrabberClickListener;
import com.todoroo.astrid.ui.DraggableListView.SwipeListener;
import org.tasks.R;
import org.tasks.tasklist.ViewHolder;
import org.tasks.tasklist.ViewHolderFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject; import javax.inject.Inject;
@ -44,9 +30,7 @@ import timber.log.Timber;
class OrderedMetadataListFragmentHelper { class OrderedMetadataListFragmentHelper {
private final DisplayMetrics metrics = new DisplayMetrics();
private final GtasksTaskListUpdater updater; private final GtasksTaskListUpdater updater;
private final ViewHolderFactory viewHolderFactory;
private final TaskDao taskDao; private final TaskDao taskDao;
private final MetadataDao metadataDao; private final MetadataDao metadataDao;
@ -56,46 +40,52 @@ class OrderedMetadataListFragmentHelper {
private GtasksList list; private GtasksList list;
@Inject @Inject
OrderedMetadataListFragmentHelper(TaskDao taskDao, MetadataDao metadataDao, OrderedMetadataListFragmentHelper(TaskDao taskDao, MetadataDao metadataDao, GtasksTaskListUpdater updater) {
GtasksTaskListUpdater updater, ViewHolderFactory viewHolderFactory) {
this.taskDao = taskDao; this.taskDao = taskDao;
this.metadataDao = metadataDao; this.metadataDao = metadataDao;
this.updater = updater; this.updater = updater;
this.viewHolderFactory = viewHolderFactory;
} }
void setTaskListFragment(TaskListFragment fragment) { void setTaskListFragment(TaskListFragment fragment) {
this.fragment = fragment; this.fragment = fragment;
} }
// --- ui component setup void beforeSetUpTaskList(Filter filter) {
updater.initialize(filter);
private Activity getActivity() {
return fragment.getActivity();
} }
private ListView getListView() { TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor) {
return fragment.getListView(); taskAdapter = new DraggableTaskAdapter(context, fragment, cursor);
}
void setUpUiComponents() { taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
TypedValue tv = new TypedValue();
getActivity().getTheme().resolveAttribute(R.attr.colorAccent, tv, false);
DraggableListView draggableListView = (DraggableListView) fragment.getListView();
draggableListView.setDragndropBackgroundColor(tv.data);
draggableListView.setDropListener(dropListener);
draggableListView.setClickListener(rowClickListener);
draggableListView.setSwipeListener(swipeListener);
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
void beforeSetUpTaskList(Filter filter) { return taskAdapter;
updater.initialize(filter);
} }
private final DropListener dropListener = new DropListener() { private final class DraggableTaskAdapter extends TaskAdapter {
private DraggableTaskAdapter(Context context, TaskListFragment activity, Cursor c) {
super(context, activity, c);
}
@Override
public int getIndent(Task task) {
return task.getValue(GtasksMetadata.INDENT);
}
@Override
public boolean canIndent(int position, Task task) {
Task parent = taskAdapter.getTask(position - 1);
return parent != null && getIndent(task) <= parent.getValue(GtasksMetadata.INDENT);
}
@Override @Override
public void drop(int from, int to) { public boolean isManuallySorted() {
return true;
}
@Override
public void moved(int from, int to) {
long targetTaskId = taskAdapter.getItemId(from); long targetTaskId = taskAdapter.getItemId(from);
if (targetTaskId <= 0) { if (targetTaskId <= 0) {
return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
@ -103,7 +93,7 @@ class OrderedMetadataListFragmentHelper {
long destinationTaskId = taskAdapter.getItemId(to); long destinationTaskId = taskAdapter.getItemId(to);
try { try {
if(to >= getListView().getCount()) { if(to >= taskAdapter.getCount()) {
updater.moveTo(list, targetTaskId, -1); updater.moveTo(list, targetTaskId, -1);
} else { } else {
updater.moveTo(list, targetTaskId, destinationTaskId); updater.moveTo(list, targetTaskId, destinationTaskId);
@ -114,20 +104,9 @@ class OrderedMetadataListFragmentHelper {
fragment.loadTaskListContent(); fragment.loadTaskListContent();
} }
};
private final SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
indent(which, 1);
}
@Override @Override
public void swipeLeft(int which) { public void indented(int which, int delta) {
indent(which, -1);
}
void indent(int which, int delta) {
long targetTaskId = taskAdapter.getItemId(which); long targetTaskId = taskAdapter.getItemId(which);
if (targetTaskId <= 0) { if (targetTaskId <= 0) {
return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
@ -139,51 +118,6 @@ class OrderedMetadataListFragmentHelper {
} }
fragment.loadTaskListContent(); fragment.loadTaskListContent();
} }
};
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;
}
taskAdapter.onClick(v);
}
};
TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor,
AtomicReference<String> sqlQueryTemplate) {
taskAdapter = new DraggableTaskAdapter(context, fragment, cursor, sqlQueryTemplate);
taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
return taskAdapter;
}
private final class DraggableTaskAdapter extends TaskAdapter {
private DraggableTaskAdapter(Context context, TaskListFragment activity,
Cursor c, AtomicReference<String> query) {
super(context, taskDao, activity, c, query, viewHolderFactory);
}
@Override
protected void adjustView(ViewHolder vh) {
int indent = vh.task.getValue(GtasksMetadata.INDENT);
vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
}
} }
private final Map<Long, ArrayList<Long>> chainedCompletions = private final Map<Long, ArrayList<Long>> chainedCompletions =

@ -108,7 +108,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi
save(); save();
} }
}); });
toolbar.inflateMenu(R.menu.tag_settings_activity); toolbar.inflateMenu(R.menu.menu_tag_settings);
toolbar.setOnMenuItemClickListener(this); toolbar.setOnMenuItemClickListener(this);
toolbar.showOverflowMenu(); toolbar.showOverflowMenu();

@ -133,7 +133,7 @@ public final class TaskEditFragment extends InjectingFragment implements Toolbar
save(); save();
} }
}); });
toolbar.inflateMenu(R.menu.task_edit_fragment); toolbar.inflateMenu(R.menu.menu_task_edit_fragment);
toolbar.setOnMenuItemClickListener(this); toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(context, toolbar); MenuColorizer.colorToolbar(context, toolbar);

@ -16,6 +16,8 @@ import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.view.MenuItemCompat; import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView; import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.ContextMenu; import android.view.ContextMenu;
@ -26,7 +28,6 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import com.todoroo.andlib.data.Callback; import com.todoroo.andlib.data.Callback;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
@ -60,8 +61,9 @@ import org.tasks.dialogs.SortDialog;
import org.tasks.gtasks.SyncAdapterHelper; import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.ForActivity; import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.injection.InjectingListFragment; import org.tasks.injection.InjectingFragment;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.tasklist.TaskListRecyclerAdapter;
import org.tasks.tasklist.ViewHolder; import org.tasks.tasklist.ViewHolder;
import org.tasks.tasklist.ViewHolderFactory; import org.tasks.tasklist.ViewHolderFactory;
import org.tasks.ui.CheckBoxes; import org.tasks.ui.CheckBoxes;
@ -83,9 +85,8 @@ import static com.todoroo.astrid.voice.VoiceInputAssistant.voiceInputAvailable;
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class TaskListFragment extends InjectingListFragment implements public class TaskListFragment extends InjectingFragment implements
SwipeRefreshLayout.OnRefreshListener, SwipeRefreshLayout.OnRefreshListener, Toolbar.OnMenuItemClickListener {
Toolbar.OnMenuItemClickListener {
public static TaskListFragment newTaskListFragment(Filter filter) { public static TaskListFragment newTaskListFragment(Filter filter) {
TaskListFragment fragment = new TaskListFragment(); TaskListFragment fragment = new TaskListFragment();
@ -124,11 +125,13 @@ public class TaskListFragment extends InjectingListFragment implements
@Inject ViewHolderFactory viewHolderFactory; @Inject ViewHolderFactory viewHolderFactory;
@BindView(R.id.swipe_layout) SwipeRefreshLayout swipeRefreshLayout; @BindView(R.id.swipe_layout) SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.swipe_layout_empty) SwipeRefreshLayout emptyView; @BindView(R.id.swipe_layout_empty) SwipeRefreshLayout emptyRefreshLayout;
@BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.task_list_coordinator) CoordinatorLayout coordinatorLayout; @BindView(R.id.task_list_coordinator) CoordinatorLayout coordinatorLayout;
@BindView(R.id.recycler_view) RecyclerView recyclerView;
private TaskAdapter taskAdapter = null; private TaskAdapter taskAdapter = null;
private TaskListRecyclerAdapter recyclerAdapter;
private final RefreshReceiver refreshReceiver = new RefreshReceiver(); private final RefreshReceiver refreshReceiver = new RefreshReceiver();
private TaskListFragmentCallbackHandler callbacks; private TaskListFragmentCallbackHandler callbacks;
@ -152,7 +155,7 @@ public class TaskListFragment extends InjectingListFragment implements
if (activity != null) { if (activity != null) {
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {
swipeRefreshLayout.setRefreshing(ongoing); swipeRefreshLayout.setRefreshing(ongoing);
emptyView.setRefreshing(ongoing); emptyRefreshLayout.setRefreshing(ongoing);
}); });
} }
} }
@ -179,15 +182,6 @@ public class TaskListFragment extends InjectingListFragment implements
component.inject(this); component.inject(this);
} }
/**
* @return view to attach to the body of the task list. must contain two
* elements, a view with id android:id/empty and a list view with id
* android:id/list. It should NOT be attached to root
*/
protected int getListBody() {
return R.layout.task_list_body_standard;
}
/** Called when loading up the activity */ /** Called when loading up the activity */
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -215,12 +209,9 @@ public class TaskListFragment extends InjectingListFragment implements
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View parent = inflater.inflate(R.layout.fragment_task_list, container, false); View parent = inflater.inflate(R.layout.fragment_task_list, container, false);
((ViewGroup) parent.findViewById(R.id.task_list_body)).addView(inflater.inflate(getListBody(), null), 0);
ButterKnife.bind(this, parent); ButterKnife.bind(this, parent);
setupRefresh(swipeRefreshLayout); setupRefresh(swipeRefreshLayout);
setupRefresh(emptyView); setupRefresh(emptyRefreshLayout);
ListView listView = (ListView) swipeRefreshLayout.findViewById(android.R.id.list);
listView.setEmptyView(emptyView);
toolbar.setTitle(filter.listingTitle); toolbar.setTitle(filter.listingTitle);
toolbar.setNavigationIcon(R.drawable.ic_menu_24dp); toolbar.setNavigationIcon(R.drawable.ic_menu_24dp);
@ -334,15 +325,6 @@ public class TaskListFragment extends InjectingListFragment implements
return taskCreator.createWithValues(filter.valuesForNewTasks, title); return taskCreator.createWithValues(filter.valuesForNewTasks, title);
} }
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView listView = getListView();
View footer = getActivity().getLayoutInflater().inflate(R.layout.task_list_footer, listView, false);
listView.addFooterView(footer, null, false);
}
private void setupRefresh(SwipeRefreshLayout layout) { private void setupRefresh(SwipeRefreshLayout layout) {
layout.setOnRefreshListener(this); layout.setOnRefreshListener(this);
layout.setColorSchemeColors(checkBoxes.getPriorityColorsArray()); layout.setColorSchemeColors(checkBoxes.getPriorityColorsArray());
@ -352,44 +334,22 @@ public class TaskListFragment extends InjectingListFragment implements
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
// We have a menu item to show in action bar. // We have a menu item to show in action bar.
final ListView listView = getListView(); // registerForContextMenu(recyclerView);
registerForContextMenu(listView);
filter.setFilterQueryOverride(null); filter.setFilterQueryOverride(null);
setListAdapter(taskAdapter); recyclerAdapter.applyToRecyclerView(recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
loadTaskListContent(); loadTaskListContent();
if (getResources().getBoolean(R.bool.two_pane_layout)) { if (getResources().getBoolean(R.bool.two_pane_layout)) {
// In dual-pane mode, the list view highlights the selected item. // In dual-pane mode, the list view highlights the selected item.
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); // listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setItemsCanFocus(false); // listView.setItemsCanFocus(false);
} }
if (this instanceof SubtasksListFragment || this instanceof SubtasksTagListFragment || this instanceof GtasksSubtaskListFragment) {
return;
}
listView.setOnItemClickListener((parent, view, position, id) -> {
if (taskAdapter != null) {
TodorooCursor<Task> cursor = (TodorooCursor<Task>) taskAdapter.getItem(position);
Task task = new Task(cursor);
if (task.isDeleted()) {
return;
}
onTaskListItemClicked(id);
}
});
} }
/*
* ======================================================================
* ============================================================ lifecycle
* ======================================================================
*/
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@ -462,20 +422,24 @@ public class TaskListFragment extends InjectingListFragment implements
Cursor taskCursor = taskAdapter.getCursor(); Cursor taskCursor = taskAdapter.getCursor();
taskCursor.requery(); taskCursor.requery();
taskAdapter.notifyDataSetChanged(); recyclerAdapter.notifyDataSetChanged();
if (recyclerAdapter.getItemCount() == 0) {
swipeRefreshLayout.setVisibility(View.GONE);
emptyRefreshLayout.setVisibility(View.VISIBLE);
} else {
swipeRefreshLayout.setVisibility(View.VISIBLE);
emptyRefreshLayout.setVisibility(View.GONE);
}
} }
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) { protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return new TaskAdapter(context, this, cursor);
return new TaskAdapter(context, taskDao, this, cursor,
taskListDataProvider.getSqlQueryTemplate(), viewHolderFactory);
} }
public static final String TAGS_METADATA_JOIN = "for_tags"; //$NON-NLS-1$ public static final String TAGS_METADATA_JOIN = "for_tags"; //$NON-NLS-1$
public static final String FILE_METADATA_JOIN = "for_actions"; //$NON-NLS-1$ public static final String FILE_METADATA_JOIN = "for_actions"; //$NON-NLS-1$
/** /**
* Fill in the Task List with current items * Fill in the Task List with current items
*/ */
@ -491,6 +455,7 @@ public class TaskListFragment extends InjectingListFragment implements
// set up list adapters // set up list adapters
taskAdapter = createTaskAdapter(currentCursor); taskAdapter = createTaskAdapter(currentCursor);
recyclerAdapter = new TaskListRecyclerAdapter(context, taskAdapter, viewHolderFactory);
} }
public Property<?>[] taskProperties() { public Property<?>[] taskProperties() {
@ -501,8 +466,6 @@ public class TaskListFragment extends InjectingListFragment implements
return filter; return filter;
} }
public void reconstructCursor() { public void reconstructCursor() {
TodorooCursor<Task> cursor = taskListDataProvider.constructCursor(filter, taskProperties()); TodorooCursor<Task> cursor = taskListDataProvider.constructCursor(filter, taskProperties());
if (cursor == null || taskAdapter == null) { if (cursor == null || taskAdapter == null) {
@ -558,13 +521,13 @@ public class TaskListFragment extends InjectingListFragment implements
timerPlugin.stopTimer(task); timerPlugin.stopTimer(task);
} }
@Override // @Override
public void onListItemClick(ListView l, View v, int position, long id) { // public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id); // super.onListItemClick(l, v, position, id);
if (getResources().getBoolean(R.bool.two_pane_layout)) { // if (getResources().getBoolean(R.bool.two_pane_layout)) {
setSelection(position); // setSelection(position);
} // }
} // }
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {

@ -7,7 +7,6 @@ package com.todoroo.astrid.adapter;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CursorAdapter; import android.widget.CursorAdapter;
@ -18,22 +17,13 @@ import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor; 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.astrid.activity.TaskListFragment; import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment; import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.tags.TaskToTagMetadata; import com.todoroo.astrid.tags.TaskToTagMetadata;
import org.tasks.R;
import org.tasks.tasklist.ViewHolder; import org.tasks.tasklist.ViewHolder;
import org.tasks.tasklist.ViewHolderFactory;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* Adapter for displaying a user's tasks as a list * Adapter for displaying a user's tasks as a list
@ -41,7 +31,7 @@ import java.util.concurrent.atomic.AtomicReference;
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class TaskAdapter extends CursorAdapter implements Filterable { public class TaskAdapter extends CursorAdapter implements Filterable, ViewHolder.ViewHolderCallbacks {
public interface OnCompletedTaskListener { public interface OnCompletedTaskListener {
void onCompletedTask(Task item, boolean newState); void onCompletedTask(Task item, boolean newState);
@ -75,94 +65,55 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
// --- instance variables // --- instance variables
private final TaskDao taskDao;
private final TaskListFragment fragment; private final TaskListFragment fragment;
private OnCompletedTaskListener onCompletedTaskListener = null; private OnCompletedTaskListener onCompletedTaskListener = null;
private final AtomicReference<String> query; public TaskAdapter(Context context, TaskListFragment fragment, Cursor c) {
private final ViewHolderFactory viewHolderFactory;
public TaskAdapter(Context context, TaskDao taskDao, TaskListFragment fragment, Cursor c,
AtomicReference<String> query, ViewHolderFactory viewHolderFactory) {
super(context, c, false); super(context, c, false);
this.taskDao = taskDao;
this.query = query;
this.fragment = fragment; this.fragment = fragment;
this.viewHolderFactory = viewHolderFactory;
} }
@Override @Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) { public View newView(Context context, Cursor cursor, ViewGroup parent) {
if (getFilterQueryProvider() != null) { throw new RuntimeException();
return getFilterQueryProvider().runQuery(constraint);
}
return fetchFiltered(query.get(), constraint, fragment.taskProperties());
} }
/** @Override
* Fetch tasks for the given filter public void bindView(View view, Context context, Cursor c) {
* @param constraint text constraint, or null throw new RuntimeException();
*/ }
private TodorooCursor<Task> fetchFiltered(String queryTemplate, CharSequence constraint,
Property<?>... properties) {
Criterion whereConstraint = null;
if(constraint != null) {
whereConstraint = Functions.upper(Task.TITLE).like("%" +
constraint.toString().toUpperCase() + "%");
}
if(queryTemplate == null) {
if(whereConstraint == null) {
return taskDao.query(Query.selectDistinct(properties));
} else {
return taskDao.query(Query.selectDistinct(properties).where(whereConstraint));
}
}
String sql;
if(whereConstraint != null) {
if(!queryTemplate.toUpperCase().contains("WHERE")) {
sql = queryTemplate + " WHERE " + whereConstraint;
} else {
sql = queryTemplate.replace("WHERE ", "WHERE " + whereConstraint + " AND ");
}
} else {
sql = queryTemplate;
}
sql = PermaSql.replacePlaceholders(sql); public int getIndent(Task task) {
return 0;
}
return taskDao.query(Query.select(properties).withQueryTemplate(sql)); public boolean canIndent(int position, Task task) {
return false;
} }
@Override public boolean isManuallySorted() {
public View newView(Context context, Cursor cursor, ViewGroup parent) { return false;
ViewGroup view = (ViewGroup) LayoutInflater.from(context) }
.inflate(R.layout.task_adapter_row_simple, parent, false);
// create view holder public void moved(int from, int to) {
viewHolderFactory.newViewHolder(view, this::onTaskCompleted);
return view;
} }
@Override public void indented(int position, int delta) {
public void bindView(View view, Context context, Cursor c) {
TodorooCursor<Task> cursor = (TodorooCursor<Task>)c;
ViewHolder viewHolder = ((ViewHolder)view.getTag());
viewHolder.bindView(cursor);
adjustView(viewHolder);
} }
protected void adjustView(ViewHolder viewHolder) { public Task getTask(int position) {
TodorooCursor<Task> c = (TodorooCursor<Task>) getCursor();
if (c != null) {
if (c.moveToPosition(position)) {
return c.toModel();
}
}
return null;
} }
public String getItemUuid(int position) { protected String getItemUuid(int position) {
TodorooCursor<Task> c = (TodorooCursor<Task>) getCursor(); TodorooCursor<Task> c = (TodorooCursor<Task>) getCursor();
if (c != null) { if (c != null) {
if (c.moveToPosition(position)) { if (c.moveToPosition(position)) {
@ -175,28 +126,13 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
} }
} }
public void onClick(View v) {
// expand view (unless deleted)
final ViewHolder viewHolder = (ViewHolder)v.getTag();
if(viewHolder.task.isDeleted()) {
return;
}
long taskId = viewHolder.task.getId();
fragment.onTaskListItemClicked(taskId);
}
/* ======================================================================
* ======================================================= event handlers
* ====================================================================== */
@Override @Override
public void notifyDataSetChanged() { public void onClick(long id) {
super.notifyDataSetChanged(); fragment.onTaskListItemClicked(id);
viewHolderFactory.updateTagMap();
} }
private void onTaskCompleted(Task task, boolean newState) { @Override
public void onCompletedTask(Task task, boolean newState) {
if (onCompletedTaskListener != null) { if (onCompletedTaskListener != null) {
onCompletedTaskListener.onCompletedTask(task, newState); onCompletedTaskListener.onCompletedTask(task, newState);
} }

@ -1,13 +1,8 @@
package com.todoroo.astrid.subtasks; package com.todoroo.astrid.subtasks;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.ListView;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
@ -21,20 +16,11 @@ import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata; import com.todoroo.astrid.data.TaskListMetadata;
import com.todoroo.astrid.ui.DraggableListView;
import com.todoroo.astrid.ui.DraggableListView.DropListener;
import com.todoroo.astrid.ui.DraggableListView.GrabberClickListener;
import com.todoroo.astrid.ui.DraggableListView.SwipeListener;
import org.tasks.R;
import org.tasks.tasklist.ViewHolder;
import org.tasks.tasklist.ViewHolderFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject; import javax.inject.Inject;
@ -42,58 +28,60 @@ import timber.log.Timber;
class AstridOrderedListFragmentHelper { class AstridOrderedListFragmentHelper {
private final DisplayMetrics metrics = new DisplayMetrics();
private final SubtasksFilterUpdater updater; private final SubtasksFilterUpdater updater;
private final TaskDao taskDao; private final TaskDao taskDao;
private final ViewHolderFactory viewHolderFactory;
private DraggableTaskAdapter taskAdapter; private DraggableTaskAdapter taskAdapter;
private TaskListFragment fragment; private TaskListFragment fragment;
private TaskListMetadata list; private TaskListMetadata list;
@Inject @Inject
AstridOrderedListFragmentHelper(SubtasksFilterUpdater updater, TaskDao taskDao, ViewHolderFactory viewHolderFactory) { AstridOrderedListFragmentHelper(SubtasksFilterUpdater updater, TaskDao taskDao) {
this.updater = updater; this.updater = updater;
this.taskDao = taskDao; this.taskDao = taskDao;
this.viewHolderFactory = viewHolderFactory;
} }
void setTaskListFragment(TaskListFragment fragment) { void setTaskListFragment(TaskListFragment fragment) {
this.fragment = fragment; this.fragment = fragment;
} }
// --- ui component setup void beforeSetUpTaskList(Filter filter) {
updater.initialize(list, filter);
private Activity getActivity() {
return fragment.getActivity();
} }
private ListView getListView() { TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor) {
return fragment.getListView(); taskAdapter = new DraggableTaskAdapter(context, fragment, cursor);
}
private Filter getFilter() { taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
return fragment.getFilter();
}
void setUpUiComponents() { return taskAdapter;
TypedValue tv = new TypedValue();
getActivity().getTheme().resolveAttribute(R.attr.colorAccent, tv, false);
DraggableListView draggableListView = (DraggableListView) fragment.getListView();
draggableListView.setDragndropBackgroundColor(tv.data);
draggableListView.setDropListener(dropListener);
draggableListView.setClickListener(rowClickListener);
draggableListView.setSwipeListener(swipeListener);
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
} }
void beforeSetUpTaskList(Filter filter) { private final class DraggableTaskAdapter extends TaskAdapter {
updater.initialize(list, filter);
} private DraggableTaskAdapter(Context context, TaskListFragment activity, Cursor c) {
super(context, activity, c);
}
@Override
public int getIndent(Task task) {
return updater.getIndentForTask(task.getUuid());
}
@Override
public boolean canIndent(int position, Task task) {
String parentUuid = taskAdapter.getItemUuid(position - 1);
int parentIndent = updater.getIndentForTask(parentUuid);
return getIndent(task) <= parentIndent;
}
private final DropListener dropListener = new DropListener() {
@Override @Override
public void drop(int from, int to) { public boolean isManuallySorted() {
return true;
}
@Override
public void moved(int from, int to) {
String targetTaskId = taskAdapter.getItemUuid(from); String targetTaskId = taskAdapter.getItemUuid(from);
if (!RemoteModel.isValidUuid(targetTaskId)) { if (!RemoteModel.isValidUuid(targetTaskId)) {
return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
@ -101,10 +89,10 @@ class AstridOrderedListFragmentHelper {
String destinationTaskId = taskAdapter.getItemUuid(to); String destinationTaskId = taskAdapter.getItemUuid(to);
try { try {
if(to >= getListView().getCount()) { if(to >= taskAdapter.getCount()) {
updater.moveTo(list, getFilter(), targetTaskId, "-1"); //$NON-NLS-1$ updater.moveTo(list, fragment.getFilter(), targetTaskId, "-1"); //$NON-NLS-1$
} else { } else {
updater.moveTo(list, getFilter(), targetTaskId, destinationTaskId); updater.moveTo(list, fragment.getFilter(), targetTaskId, destinationTaskId);
} }
} catch (Exception e) { } catch (Exception e) {
Timber.e(e, e.getMessage()); Timber.e(e, e.getMessage());
@ -113,26 +101,15 @@ class AstridOrderedListFragmentHelper {
fragment.reconstructCursor(); fragment.reconstructCursor();
fragment.loadTaskListContent(); fragment.loadTaskListContent();
} }
};
private final SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
indent(which, 1);
}
@Override @Override
public void swipeLeft(int which) { public void indented(int which, int delta) {
indent(which, -1);
}
void indent(int which, int delta) {
String targetTaskId = taskAdapter.getItemUuid(which); String targetTaskId = taskAdapter.getItemUuid(which);
if (!RemoteModel.isValidUuid(targetTaskId)) { if (!RemoteModel.isValidUuid(targetTaskId)) {
return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
} }
try { try {
updater.indent(list, getFilter(), targetTaskId, delta); updater.indent(list, fragment.getFilter(), targetTaskId, delta);
} catch (Exception e) { } catch (Exception e) {
Timber.e(e, e.getMessage()); Timber.e(e, e.getMessage());
} }
@ -140,51 +117,6 @@ class AstridOrderedListFragmentHelper {
fragment.reconstructCursor(); fragment.reconstructCursor();
fragment.loadTaskListContent(); fragment.loadTaskListContent();
} }
};
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;
}
taskAdapter.onClick(v);
}
};
TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor,
AtomicReference<String> sqlQueryTemplate) {
taskAdapter = new DraggableTaskAdapter(context, fragment, cursor, sqlQueryTemplate);
taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
return taskAdapter;
}
private final class DraggableTaskAdapter extends TaskAdapter {
private DraggableTaskAdapter(Context context, TaskListFragment activity, Cursor c,
AtomicReference<String> query) {
super(context, taskDao, activity, c, query, viewHolderFactory);
}
@Override
protected void adjustView(ViewHolder vh) {
int indent = updater.getIndentForTask(vh.task.getUuid());
vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
}
} }
private final Map<String, ArrayList<String>> chainedCompletions = private final Map<String, ArrayList<String>> chainedCompletions =
@ -250,13 +182,13 @@ class AstridOrderedListFragmentHelper {
} }
void onCreateTask(String uuid) { void onCreateTask(String uuid) {
updater.onCreateTask(list, getFilter(), uuid); updater.onCreateTask(list, fragment.getFilter(), uuid);
fragment.reconstructCursor(); fragment.reconstructCursor();
fragment.loadTaskListContent(); fragment.loadTaskListContent();
} }
void onDeleteTask(Task task) { void onDeleteTask(Task task) {
updater.onDeleteTask(list, getFilter(), task.getUuid()); updater.onDeleteTask(list, fragment.getFilter(), task.getUuid());
taskAdapter.notifyDataSetInvalidated(); taskAdapter.notifyDataSetInvalidated();
} }
} }

@ -7,10 +7,7 @@ package com.todoroo.astrid.subtasks;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View;
import android.widget.ListView;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.activity.TaskListFragment;
@ -22,7 +19,6 @@ import com.todoroo.astrid.dao.TaskListMetadataDao;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata; import com.todoroo.astrid.data.TaskListMetadata;
import org.tasks.R;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
@ -44,8 +40,6 @@ public class SubtasksListFragment extends TaskListFragment {
return fragment; return fragment;
} }
private int lastVisibleIndex = -1;
@Inject Preferences preferences; @Inject Preferences preferences;
@Inject @ForApplication Context context; @Inject @ForApplication Context context;
@Inject TaskListMetadataDao taskListMetadataDao; @Inject TaskListMetadataDao taskListMetadataDao;
@ -60,18 +54,6 @@ public class SubtasksListFragment extends TaskListFragment {
helper.setTaskListFragment(this); helper.setTaskListFragment(this);
} }
@Override
protected int getListBody() {
return R.layout.task_list_body_subtasks;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
helper.setUpUiComponents();
}
@Override @Override
public void setTaskAdapter() { public void setTaskAdapter() {
helper.setList(initializeTaskListMetadata()); helper.setList(initializeTaskListMetadata());
@ -108,22 +90,6 @@ public class SubtasksListFragment extends TaskListFragment {
return taskListMetadata; return taskListMetadata;
} }
@Override
public void onPause() {
super.onPause();
lastVisibleIndex = getListView().getFirstVisiblePosition();
}
@Override
public void onResume() {
super.onResume();
ListView listView = getListView();
if (lastVisibleIndex >= 0) {
listView.setSelection(lastVisibleIndex);
}
unregisterForContextMenu(listView);
}
@Override @Override
public void onTaskCreated(String uuid) { public void onTaskCreated(String uuid) {
helper.onCreateTask(uuid); helper.onCreateTask(uuid);
@ -137,7 +103,7 @@ public class SubtasksListFragment extends TaskListFragment {
@Override @Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) { protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate()); return helper.createTaskAdapter(theme.wrap(context), cursor);
} }
@Override @Override

@ -20,7 +20,6 @@ import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata; import com.todoroo.astrid.data.TaskListMetadata;
import org.tasks.R;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.tasklist.TagListFragment; import org.tasks.tasklist.TagListFragment;
@ -42,8 +41,6 @@ public class SubtasksTagListFragment extends TagListFragment {
@Inject Theme theme; @Inject Theme theme;
@Inject AstridOrderedListFragmentHelper helper; @Inject AstridOrderedListFragmentHelper helper;
private int lastVisibleIndex = -1;
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Activity activity) {
super.onAttach(activity); super.onAttach(activity);
@ -51,18 +48,6 @@ public class SubtasksTagListFragment extends TagListFragment {
helper.setTaskListFragment(this); helper.setTaskListFragment(this);
} }
@Override
protected int getListBody() {
return R.layout.task_list_body_subtasks;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
helper.setUpUiComponents();
}
@Override @Override
public void setTaskAdapter() { public void setTaskAdapter() {
String tdId = tagData.getUuid(); String tdId = tagData.getUuid();
@ -78,21 +63,6 @@ public class SubtasksTagListFragment extends TagListFragment {
super.setTaskAdapter(); super.setTaskAdapter();
} }
@Override
public void onPause() {
super.onPause();
lastVisibleIndex = getListView().getFirstVisiblePosition();
}
@Override
public void onResume() {
super.onResume();
if (lastVisibleIndex >= 0) {
getListView().setSelection(lastVisibleIndex);
}
unregisterForContextMenu(getListView());
}
@Override @Override
public void onTaskCreated(String uuid) { public void onTaskCreated(String uuid) {
helper.onCreateTask(uuid); helper.onCreateTask(uuid);
@ -106,7 +76,7 @@ public class SubtasksTagListFragment extends TagListFragment {
@Override @Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) { protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate()); return helper.createTaskAdapter(theme.wrap(context), cursor);
} }
@Override @Override

@ -128,6 +128,10 @@ public final class TagService {
return tagList; return tagList;
} }
public TagData getTagByUuid(String uuid) {
return tagDataDao.getByUuid(uuid);
}
/** /**
* If a tag already exists in the database that case insensitively matches the * If a tag already exists in the database that case insensitively matches the
* given tag, return that. Otherwise, return the argument * given tag, return that. Otherwise, return the argument

@ -1,530 +0,0 @@
/*
* Copyright (c) 2010 CommonsWare, LLC
* Portions Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.todoroo.astrid.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import com.todoroo.astrid.utility.Flags;
import org.tasks.R;
import timber.log.Timber;
public class DraggableListView extends ListView {
private static final int SWIPE_THRESHOLD = 40;
private static final int MOVEMENT_THRESHOLD = 30;
// --- drag status
private float mTouchStartX, mTouchCurrentX, mTouchStartY, mTouchCurrentY;
private boolean mDragging = false;
private int mDragPos; // which item is being dragged
private int mFirstDragPos; // where was the dragged item originally
private Point mDragPoint; // at what offset inside the item did the user grab it
private Point mCoordOffset; // the difference between screen coordinates and coordinates in this view
// --- drag drawing
private ImageView mDragView;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowParams;
private int mUpperBound;
private int mLowerBound;
private int mHeight;
private final Rect mTempRect = new Rect();
private Bitmap mDragBitmap;
private final int mTouchSlop;
private int dragndropBackgroundColor = 0x00000000;
// --- listeners
private DropListener mDropListener;
private SwipeListener mSwipeListener;
private GrabberClickListener mClickListener;
// --- other instance variables
private int mItemHeightNormal = -1;
private Thread dragThread = null;
// --- constructors
public DraggableListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DraggableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.TouchListView, 0, 0);
mItemHeightNormal = a.getDimensionPixelSize(
R.styleable.TouchListView_normal_height, 0);
dragndropBackgroundColor = a.getColor(
R.styleable.TouchListView_dragndrop_background, 0x00000000);
a.recycle();
}
setSelector(R.drawable.none);
}
public void setItemHightNormal(int itemHeightNormal) {
this.mItemHeightNormal = itemHeightNormal;
}
/*
* pointToPosition() doesn't consider invisible views, but we need to, so
* implement a slightly different version.
*/
private int myPointToPosition(int x, int y) {
Rect frame = mTempRect;
final int count = getChildCount();
for (int i = count - 1; i >= 0; i--) {
final View child = getChildAt(i);
child.getHitRect(frame);
if (frame.contains(x, y)) {
return getFirstVisiblePosition() + i;
}
}
return INVALID_POSITION;
}
private int getItemForPosition(int y) {
int adjustedy = y - mDragPoint.y - (mItemHeightNormal / 2);
int pos = myPointToPosition(0, adjustedy);
if (pos >= 0) {
if (pos <= mFirstDragPos) {
pos += 1;
}
} else if (adjustedy < 0) {
pos = 0;
}
return pos;
}
private void adjustScrollBounds(int y) {
if (y >= mHeight / 3) {
mUpperBound = mHeight / 3;
}
if (y <= mHeight * 2 / 3) {
mLowerBound = mHeight * 2 / 3;
}
}
/*
* Restore size and visibility for all list items
*/
private void unExpandViews() {
for (int i = 0;; i++) {
View v = getChildAt(i);
if (v == null) {
layoutChildren(); // force children to be recreated where needed
v = getChildAt(i);
if (v == null) {
break;
}
}
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = LayoutParams.WRAP_CONTENT;
v.setLayoutParams(params);
v.setVisibility(View.VISIBLE);
v.setPadding(0, 0, 0, 0);
}
}
/*
* Adjust visibility and size to make it appear as though an item is being
* dragged around and other items are making room for it: If dropping the
* item would result in it still being in the same place, then make the
* dragged listitem's size normal, but make the item invisible. Otherwise,
* if the dragged list item is still on screen, make it as small as possible
* and expand the item below the insert point. If the dragged item is not on
* screen, only expand the item below the current insert point.
*/
private void doExpansion() {
int childnum = mDragPos - getFirstVisiblePosition();
if (mDragPos > mFirstDragPos) {
childnum++;
}
View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());
for (int i = 0;; i++) {
View vv = getChildAt(i);
if (vv == null) {
break;
}
int height = LayoutParams.WRAP_CONTENT;
int marginTop = 0;
int visibility = View.VISIBLE;
if (vv.equals(first)) {
// processing the item that is being dragged
if (mDragPos == mFirstDragPos) {
// hovering over the original location
visibility = View.INVISIBLE;
} else {
// not hovering over it
height = 1;
}
} else if (i == childnum) {
if (mDragPos < getCount() - 1) {
marginTop = mItemHeightNormal;
height = 2 * mItemHeightNormal;
}
}
ViewGroup.LayoutParams params = vv.getLayoutParams();
params.height = height;
vv.setLayoutParams(params);
vv.setVisibility(visibility);
vv.setPadding(0, marginTop, 0, 0);
}
// Request re-layout since we changed the items layout
// and not doing this would cause bogus hitbox calculation
// in myPointToPosition
layoutChildren();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
mTouchCurrentX = ev.getX();
mTouchCurrentY = ev.getY();
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if(mDragging) {
stopDragging();
} else {
if (dragThread != null && mClickListener != null) {
dragThread.interrupt();
dragThread = null;
if (action == MotionEvent.ACTION_UP) {
mClickListener.onClick(viewAtPosition());
}
}
else if (mSwipeListener != null &&
Math.abs(mTouchCurrentY - mTouchStartY) < MOVEMENT_THRESHOLD) {
int dragPos = pointToPosition((int)mTouchCurrentX, (int)mTouchCurrentY);
if (mTouchCurrentX > mTouchStartX + SWIPE_THRESHOLD) {
mSwipeListener.swipeRight(dragPos);
} else if (mTouchCurrentX < mTouchStartX - SWIPE_THRESHOLD) {
mSwipeListener.swipeLeft(dragPos);
}
}
}
if(dragThread != null) {
dragThread.interrupt();
dragThread = null;
}
break;
case MotionEvent.ACTION_DOWN:
dragThread = new Thread(new DragRunnable(ev));
dragThread.start();
stopDragging();
mTouchStartX = ev.getX();
mTouchStartY = ev.getY();
case MotionEvent.ACTION_MOVE:
if(mDragging) {
dragView(ev);
}
// detect scrolling
if(dragThread != null && (Math.abs(mTouchCurrentX - mTouchStartX) > MOVEMENT_THRESHOLD ||
Math.abs(mTouchCurrentY - mTouchStartY) > MOVEMENT_THRESHOLD)) {
dragThread.interrupt();
dragThread = null;
}
break;
}
if(mDragging) {
return true;
}
return super.onTouchEvent(ev);
}
private View viewAtPosition() {
int itemNum = pointToPosition((int) mTouchCurrentX, (int) mTouchCurrentY);
if (itemNum == AdapterView.INVALID_POSITION) {
return null;
}
return getChildAt(itemNum - getFirstVisiblePosition());
}
// --- drag logic
private class DragRunnable implements Runnable {
private final MotionEvent ev;
public DragRunnable(MotionEvent ev) {
this.ev = ev;
}
@Override
public void run() {
try {
Thread.sleep(300L);
post(() -> initiateDrag(ev));
Thread.sleep(1000L);
post(() -> {
stopDragging();
dragThread = null;
Vibrator v = (Vibrator) getContext().getSystemService(
Context.VIBRATOR_SERVICE);
v.vibrate(50);
mClickListener.onLongClick(viewAtPosition());
});
} catch (InterruptedException e) {
// bye!
Timber.v(e, e.getMessage());
}
}
}
private void initiateDrag(MotionEvent ev) {
int x = (int) mTouchCurrentX;
int y = (int) mTouchCurrentY;
int itemNum = pointToPosition(x, y);
if (itemNum == AdapterView.INVALID_POSITION) {
return;
}
View item = getChildAt(itemNum - getFirstVisiblePosition());
mDragPoint = new Point(x - item.getLeft(), y - item.getTop());
mCoordOffset = new Point((int)ev.getRawX() - x, (int)ev.getRawY() - y);
item.setDrawingCacheEnabled(true);
// Create a copy of the drawing cache so that it does not get
// recycled by the framework when the list tries to clean up memory
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
item.setDrawingCacheEnabled(false);
Rect listBounds = new Rect();
getGlobalVisibleRect(listBounds, null);
startDragging(bitmap, listBounds.left, y);
mDragPos = itemNum;
mFirstDragPos = mDragPos;
mHeight = getHeight();
int touchSlop = mTouchSlop;
mUpperBound = Math.min(y - touchSlop, mHeight / 3);
mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3);
Vibrator v = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(50);
}
private void startDragging(Bitmap bm, int x, int y) {
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP | Gravity.START;
mWindowParams.x = x;
mWindowParams.y = y - mDragPoint.y + mCoordOffset.y;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.windowAnimations = 0;
ImageView v = new ImageView(getContext());
v.setBackgroundColor(dragndropBackgroundColor);
v.setImageBitmap(bm);
mDragBitmap = bm;
mWindowManager = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);
mWindowManager.addView(v, mWindowParams);
mDragView = v;
mDragging = true;
Flags.set(Flags.TLFP_NO_INTERCEPT_TOUCH);
}
private void dragView(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
mWindowParams.y = y - mDragPoint.y + mCoordOffset.y;
if (mDragPos == mFirstDragPos && x > mTouchStartX + SWIPE_THRESHOLD) {
mDragView.setPadding(30, 1, 0, 1);
} else if (mDragPos == mFirstDragPos && x < mTouchStartX - SWIPE_THRESHOLD) {
mDragView.setPadding(-30, 2, 0, 2);
} else {
mDragView.setPadding(0, 0, 0, 0);
}
mWindowManager.updateViewLayout(mDragView, mWindowParams);
int itemnum = getItemForPosition(y);
if (itemnum >= 0) {
if (ev.getAction() == MotionEvent.ACTION_DOWN
|| itemnum != mDragPos) {
mDragPos = itemnum;
doExpansion();
}
int speed = 0;
adjustScrollBounds(y);
if (y > mLowerBound) {
// scroll the list up a bit
speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
} else if (y < mUpperBound) {
// scroll the list down a bit
speed = y < mUpperBound / 2 ? -16 : -4;
}
if (speed != 0) {
int ref = pointToPosition(0, mHeight / 2);
if (ref == AdapterView.INVALID_POSITION) {
// we hit a divider or an invisible view, check
// somewhere else
ref = pointToPosition(0, mHeight / 2
+ getDividerHeight() + 64);
}
View v = getChildAt(ref - getFirstVisiblePosition());
if (v != null) {
int pos = v.getTop();
setSelectionFromTop(ref, pos - speed);
}
}
}
}
private void stopDragging() {
if (mDragBitmap != null) {
mDragBitmap.recycle();
mDragBitmap = null;
}
unExpandViews();
if (mDragView != null) {
WindowManager wm = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);
wm.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
}
if(mDragging) {
if (mSwipeListener != null && mDragPos == mFirstDragPos) {
if (mTouchCurrentX > mTouchStartX + SWIPE_THRESHOLD) {
mSwipeListener.swipeRight(mFirstDragPos);
} else if (mTouchCurrentX < mTouchStartX - SWIPE_THRESHOLD) {
mSwipeListener.swipeLeft(mFirstDragPos);
}
} else if(mDropListener != null && mDragPos != mFirstDragPos &&
mDragPos >= 0 && mDragPos < getCount()) {
if(mFirstDragPos < mDragPos) {
mDragPos++;
}
mDropListener.drop(mFirstDragPos, mDragPos);
}
}
mDragging = false;
Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH);
}
// --- getters and setters
public void setDropListener(DropListener l) {
mDropListener = l;
}
public void setSwipeListener(SwipeListener l) {
mSwipeListener = l;
}
public void setClickListener(GrabberClickListener listener) {
this.mClickListener = listener;
}
public void setDragndropBackgroundColor(int color) {
this.dragndropBackgroundColor = color;
}
@Override
final public void addHeaderView(View v, Object data, boolean isSelectable) {
throw new RuntimeException(
"Headers are not supported with TouchListView");
}
@Override
final public void addHeaderView(View v) {
throw new RuntimeException(
"Headers are not supported with TouchListView");
}
public interface GrabberClickListener {
void onClick(View v);
void onLongClick(View v);
}
public interface DropListener {
void drop(int from, int to);
}
public interface SwipeListener {
void swipeLeft(int which);
void swipeRight(int which);
}
}

@ -70,7 +70,7 @@ public class FilterSettingsActivity extends ThemedInjectingAppCompatActivity imp
save(); save();
} }
}); });
toolbar.inflateMenu(R.menu.tag_settings_activity); toolbar.inflateMenu(R.menu.menu_tag_settings);
toolbar.setOnMenuItemClickListener(this); toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(this, toolbar); MenuColorizer.colorToolbar(this, toolbar);

@ -109,7 +109,7 @@ public class TagSettingsActivity extends ThemedInjectingAppCompatActivity implem
save(); save();
} }
}); });
toolbar.inflateMenu(R.menu.tag_settings_activity); toolbar.inflateMenu(R.menu.menu_tag_settings);
toolbar.setOnMenuItemClickListener(this); toolbar.setOnMenuItemClickListener(this);
toolbar.showOverflowMenu(); toolbar.showOverflowMenu();

@ -39,10 +39,6 @@ public class TaskListDataProvider {
this.preferences = preferences; this.preferences = preferences;
} }
public AtomicReference<String> getSqlQueryTemplate() {
return sqlQueryTemplate;
}
public TodorooCursor<Task> constructCursor(Filter filter, Property<?>[] properties) { public TodorooCursor<Task> constructCursor(Filter filter, Property<?>[] properties) {
Criterion tagsJoinCriterion = Criterion.and( Criterion tagsJoinCriterion = Criterion.and(
Field.field(TAGS_METADATA_JOIN + "." + Metadata.KEY.name).eq(TaskToTagMetadata.KEY), //$NON-NLS-1$ Field.field(TAGS_METADATA_JOIN + "." + Metadata.KEY.name).eq(TaskToTagMetadata.KEY), //$NON-NLS-1$

@ -46,16 +46,13 @@ public class TagFormatter {
TypedValue typedValue = new TypedValue(); TypedValue typedValue = new TypedValue();
context.getResources().getValue(R.dimen.tag_characters, typedValue, true); context.getResources().getValue(R.dimen.tag_characters, typedValue, true);
tagCharacters = typedValue.getFloat(); tagCharacters = typedValue.getFloat();
}
public void updateTagMap() {
tagMap.clear();
for (TagData tagData : tagService.getTagList()) { for (TagData tagData : tagService.getTagList()) {
tagMap.put(tagData.getUuid(), tagData); tagMap.put(tagData.getUuid(), tagData);
} }
} }
public CharSequence getTagString(List<String> tagUuids) { CharSequence getTagString(List<String> tagUuids) {
Iterable<TagData> t = filter(transform(tagUuids, uuidToTag), Predicates.notNull()); Iterable<TagData> t = filter(transform(tagUuids, uuidToTag), Predicates.notNull());
List<TagData> firstFourByName = orderByName.leastOf(t, 4); List<TagData> firstFourByName = orderByName.leastOf(t, 4);
int numTags = firstFourByName.size(); int numTags = firstFourByName.size();
@ -99,7 +96,16 @@ public class TagFormatter {
}; };
} }
private final Function<String, TagData> uuidToTag = tagMap::get; private TagData getTag(String uuid) {
TagData tagData = tagMap.get(uuid);
if (tagData == null) {
tagData = tagService.getTagByUuid(uuid);
tagMap.put(uuid, tagData);
}
return tagData;
}
private final Function<String, TagData> uuidToTag = this::getTag;
private final Ordering<TagData> orderByName = new Ordering<TagData>() { private final Ordering<TagData> orderByName = new Ordering<TagData>() {
@Override @Override

@ -96,13 +96,6 @@ public class TagListFragment extends TaskListFragment {
} }
} }
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnKeyListener(null);
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);

@ -0,0 +1,134 @@
package org.tasks.tasklist;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.utility.Flags;
import org.tasks.R;
public class TaskListRecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {
private final Context context;
private final TaskAdapter adapter;
private final ViewHolderFactory viewHolderFactory;
private final ItemTouchHelper itemTouchHelper;
public TaskListRecyclerAdapter(Context context, TaskAdapter adapter, ViewHolderFactory viewHolderFactory) {
this.context = context;
this.adapter = adapter;
this.viewHolderFactory = viewHolderFactory;
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback());
}
public void applyToRecyclerView(RecyclerView recyclerView) {
recyclerView.setAdapter(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewGroup view = (ViewGroup) LayoutInflater.from(context)
.inflate(R.layout.task_adapter_row_simple, parent, false);
return viewHolderFactory.newViewHolder(view, adapter);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Cursor cursor = adapter.getCursor();
cursor.moveToPosition(position);
holder.bindView((TodorooCursor<Task>) cursor);
holder.setIndent(adapter.getIndent(holder.task));
}
@Override
public int getItemCount() {
return adapter.getCount();
}
private class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
private int from = -1;
private int to = -1;
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (!adapter.isManuallySorted()) {
return makeMovementFlags(0, 0);
}
ViewHolder vh = (ViewHolder) viewHolder;
int indentFlags = 0;
if (vh.isIndented()) {
indentFlags |= ItemTouchHelper.LEFT;
}
int position = vh.getAdapterPosition();
if (position > 0 && adapter.canIndent(position, vh.task)) {
indentFlags |= ItemTouchHelper.RIGHT;
}
return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, indentFlags);
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
Flags.set(Flags.TLFP_NO_INTERCEPT_TOUCH);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
int fromPosition = source.getAdapterPosition();
int toPosition = target.getAdapterPosition();
if (from == -1) {
from = fromPosition;
}
to = toPosition;
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return .2f;
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
float shiftSize = ((ViewHolder) viewHolder).getShiftSize();
dX = Math.max(-shiftSize, Math.min(shiftSize, dX));
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (from >= 0 && from != to) {
if (from < to) {
to++;
}
adapter.moved(from, to);
}
from = -1;
to = -1;
Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH);
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
adapter.indented(
viewHolder.getAdapterPosition(),
direction == ItemTouchHelper.RIGHT ? 1 : -1);
}
}
}

@ -4,10 +4,12 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.graphics.Paint; import android.graphics.Paint;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.text.util.Linkify; import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
@ -37,25 +39,29 @@ import butterknife.ButterKnife;
import timber.log.Timber; import timber.log.Timber;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop;
/** /**
* View Holder saves a lot of findViewById lookups. * View Holder saves a lot of findViewById lookups.
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
*/ */
public class ViewHolder { public class ViewHolder extends RecyclerView.ViewHolder {
public interface OnCompletedTaskCallback { public interface ViewHolderCallbacks {
void onCompletedTask(Task task, boolean newState); void onCompletedTask(Task task, boolean newState);
void onClick(long id);
} }
@BindView(R.id.rowBody) public ViewGroup rowBody; @BindView(R.id.row) public ViewGroup row;
@BindView(R.id.rowBody) ViewGroup rowBody;
@BindView(R.id.title) TextView nameView; @BindView(R.id.title) TextView nameView;
@BindView(R.id.completeBox) public CheckableImageView completeBox; @BindView(R.id.completeBox) CheckableImageView completeBox;
@BindView(R.id.due_date) public TextView dueDate; @BindView(R.id.due_date) public TextView dueDate;
@BindView(R.id.tag_block) TextView tagBlock; @BindView(R.id.tag_block) TextView tagBlock;
@BindView(R.id.taskActionContainer) public View taskActionContainer; @BindView(R.id.taskActionContainer) View taskActionContainer;
@BindView(R.id.taskActionIcon) public ImageView taskActionIcon; @BindView(R.id.taskActionIcon) ImageView taskActionIcon;
public Task task; public Task task;
@ -70,14 +76,18 @@ public class ViewHolder {
private final int textColorHint; private final int textColorHint;
private final TaskDao taskDao; private final TaskDao taskDao;
private final DialogBuilder dialogBuilder; private final DialogBuilder dialogBuilder;
private final OnCompletedTaskCallback callback; private final ViewHolderCallbacks callback;
private final DisplayMetrics metrics;
private final int textColorOverdue; private final int textColorOverdue;
private Pair<Float, Float> lastTouchYRawY = new Pair<>(0f, 0f); private Pair<Float, Float> lastTouchYRawY = new Pair<>(0f, 0f);
private int indent;
public ViewHolder(Context context, ViewGroup view, boolean showFullTaskTitle, int fontSize, public ViewHolder(Context context, ViewGroup view, boolean showFullTaskTitle, int fontSize,
CheckBoxes checkBoxes, TagFormatter tagFormatter, CheckBoxes checkBoxes, TagFormatter tagFormatter,
int textColorOverdue, int textColorSecondary, int textColorHint, TaskDao taskDao, int textColorOverdue, int textColorSecondary, int textColorHint, TaskDao taskDao,
DialogBuilder dialogBuilder, OnCompletedTaskCallback callback, int minRowHeight) { DialogBuilder dialogBuilder, ViewHolderCallbacks callback, int minRowHeight,
DisplayMetrics metrics) {
super(view);
this.context = context; this.context = context;
this.fontSize = fontSize; this.fontSize = fontSize;
this.checkBoxes = checkBoxes; this.checkBoxes = checkBoxes;
@ -88,6 +98,7 @@ public class ViewHolder {
this.taskDao = taskDao; this.taskDao = taskDao;
this.dialogBuilder = dialogBuilder; this.dialogBuilder = dialogBuilder;
this.callback = callback; this.callback = callback;
this.metrics = metrics;
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
task = new Task(); task = new Task();
@ -108,7 +119,30 @@ public class ViewHolder {
addListeners(); addListeners();
} }
public void bindView(TodorooCursor<Task> cursor) { public void setIndent(int indent) {
this.indent = indent;
int indentSize = getIndentSize(indent);
if (atLeastLollipop()) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) row.getLayoutParams();
layoutParams.setMarginStart(indentSize);
} else {
rowBody.setPadding(indentSize, row.getPaddingTop(), 0, row.getPaddingBottom());
}
}
float getShiftSize() {
return 20 * metrics.density;
}
private int getIndentSize(int indent) {
return Math.round(indent * getShiftSize());
}
boolean isIndented() {
return indent > 0;
}
void bindView(TodorooCursor<Task> cursor) {
tagsString = cursor.get(TaskAdapter.TAGS); tagsString = cursor.get(TaskAdapter.TAGS);
hasFiles = cursor.get(TaskAdapter.FILE_ID_PROPERTY) > 0; hasFiles = cursor.get(TaskAdapter.FILE_ID_PROPERTY) > 0;
hasNotes = cursor.get(TaskAdapter.HAS_NOTES_PROPERTY) > 0; hasNotes = cursor.get(TaskAdapter.HAS_NOTES_PROPERTY) > 0;
@ -120,7 +154,7 @@ public class ViewHolder {
setTaskAppearance(); setTaskAppearance();
} }
public void setMinimumHeight(int minRowHeight) { private void setMinimumHeight(int minRowHeight) {
if (fontSize < 16) { if (fontSize < 16) {
rowBody.setMinimumHeight(0); rowBody.setMinimumHeight(0);
completeBox.setMinimumHeight(0); completeBox.setMinimumHeight(0);
@ -167,7 +201,7 @@ public class ViewHolder {
return LinkActionExposer.getActionsForTask(context, task, hasFiles, hasNotes); return LinkActionExposer.getActionsForTask(context, task, hasFiles, hasNotes);
} }
public void setTaskAppearance() { private void setTaskAppearance() {
boolean completed = task.isCompleted(); boolean completed = task.isCompleted();
TextView name = nameView; TextView name = nameView;
@ -258,6 +292,12 @@ public class ViewHolder {
completeBox.setOnTouchListener(otl); completeBox.setOnTouchListener(otl);
completeBox.setOnClickListener(completeBoxListener); completeBox.setOnClickListener(completeBoxListener);
rowBody.setOnClickListener(view -> {
if (!task.isDeleted()) {
callback.onClick(task.getId());
}
});
if (taskActionContainer != null) { if (taskActionContainer != null) {
taskActionContainer.setOnClickListener(v -> { taskActionContainer.setOnClickListener(v -> {
TaskAction action = (TaskAction) taskActionIcon.getTag(); TaskAction action = (TaskAction) taskActionIcon.getTag();

@ -30,6 +30,7 @@ public class ViewHolderFactory {
private final TaskDao taskDao; private final TaskDao taskDao;
private final DialogBuilder dialogBuilder; private final DialogBuilder dialogBuilder;
private final int minRowHeight; private final int minRowHeight;
private final DisplayMetrics metrics;
@Inject @Inject
public ViewHolderFactory(@ForActivity Context context, Preferences preferences, public ViewHolderFactory(@ForActivity Context context, Preferences preferences,
@ -45,18 +46,13 @@ public class ViewHolderFactory {
textColorOverdue = getColor(context, R.color.overdue); textColorOverdue = getColor(context, R.color.overdue);
showFullTaskTitle = preferences.getBoolean(R.string.p_fullTaskTitle, false); showFullTaskTitle = preferences.getBoolean(R.string.p_fullTaskTitle, false);
fontSize = preferences.getIntegerFromString(R.string.p_fontSize, 18); fontSize = preferences.getIntegerFromString(R.string.p_fontSize, 18);
tagFormatter.updateTagMap(); metrics = context.getResources().getDisplayMetrics();
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
minRowHeight = (int) (metrics.density * 40); minRowHeight = (int) (metrics.density * 40);
} }
public ViewHolder newViewHolder(ViewGroup viewGroup, ViewHolder.OnCompletedTaskCallback onCompletedTaskCallback) { ViewHolder newViewHolder(ViewGroup viewGroup, ViewHolder.ViewHolderCallbacks callbacks) {
return new ViewHolder(context, viewGroup, showFullTaskTitle, fontSize, checkBoxes, return new ViewHolder(context, viewGroup, showFullTaskTitle, fontSize, checkBoxes,
tagFormatter, textColorOverdue, textColorSecondary, textColorHint, taskDao, tagFormatter, textColorOverdue, textColorSecondary, textColorHint, taskDao,
dialogBuilder, onCompletedTaskCallback, minRowHeight); dialogBuilder, callbacks, minRowHeight, metrics);
}
public void updateTagMap() {
tagFormatter.updateTagMap();
} }
} }

@ -25,11 +25,11 @@ public class ThemeCache {
public ThemeCache(Context context) { public ThemeCache(Context context) {
Resources resources = context.getResources(); Resources resources = context.getResources();
themes.add(new ThemeBase(context.getString(R.string.theme_light), 0, getColor(context, R.color.md_background_light), AppCompatDelegate.MODE_NIGHT_NO)); themes.add(new ThemeBase(context.getString(R.string.theme_light), 0, getColor(context, R.color.grey_50), AppCompatDelegate.MODE_NIGHT_NO));
themes.add(new ThemeBase(context.getString(R.string.theme_black), 1, getColor(context, R.color.widget_background_black), AppCompatDelegate.MODE_NIGHT_YES)); themes.add(new ThemeBase(context.getString(R.string.theme_black), 1, getColor(context, R.color.widget_background_black), AppCompatDelegate.MODE_NIGHT_YES));
themes.add(new ThemeBase(context.getString(R.string.theme_dark), 2, getColor(context, R.color.md_background_dark), AppCompatDelegate.MODE_NIGHT_YES)); themes.add(new ThemeBase(context.getString(R.string.theme_dark), 2, getColor(context, R.color.md_background_dark), AppCompatDelegate.MODE_NIGHT_YES));
themes.add(new ThemeBase(context.getString(R.string.theme_wallpaper), 3, getColor(context, R.color.black_38), AppCompatDelegate.MODE_NIGHT_YES)); themes.add(new ThemeBase(context.getString(R.string.theme_wallpaper), 3, getColor(context, R.color.black_38), AppCompatDelegate.MODE_NIGHT_YES));
themes.add(new ThemeBase(context.getString(R.string.theme_day_night), 4, getColor(context, R.color.md_background_light), AppCompatDelegate.MODE_NIGHT_AUTO)); themes.add(new ThemeBase(context.getString(R.string.theme_day_night), 4, getColor(context, R.color.grey_50), AppCompatDelegate.MODE_NIGHT_AUTO));
String[] colorNames = resources.getStringArray(R.array.colors); String[] colorNames = resources.getStringArray(R.array.colors);
for (int i = 0 ; i < ThemeColor.COLORS.length ; i++) { for (int i = 0 ; i < ThemeColor.COLORS.length ; i++) {

@ -4,7 +4,7 @@ import org.tasks.R;
public class WidgetTheme { public class WidgetTheme {
public static final int[] BACKGROUNDS = new int[] { public static final int[] BACKGROUNDS = new int[] {
R.color.md_background_light, R.color.grey_50,
R.color.widget_background_black, R.color.widget_background_black,
R.color.md_background_dark R.color.md_background_dark
}; };

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1px"
app:cardBackgroundColor="?attr/asContentBackground"
app:cardCornerRadius="0dp"
app:cardElevation="?attr/card_elevation"
app:cardPreventCornerOverlap="false">
<include layout="@layout/task_adapter_row_body" />
</android.support.v7.widget.CardView>

@ -2,13 +2,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical">
android:background="?asContentBackground">
<include layout="@layout/toolbar" /> <include layout="@layout/toolbar" />
<android.support.v7.widget.RecyclerView <android.support.v7.widget.RecyclerView
android:id="@android:id/list" android:id="@android:id/list"
android:background="?asWindowBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>

@ -15,7 +15,9 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="100" android:layout_weight="100"
android:background="?attr/asContentBackground" android:background="?attr/asContentBackground"
android:overScrollMode="never"> android:elevation="0dp"
android:overScrollMode="never"
android:scrollbarStyle="outsideOverlay">
<LinearLayout <LinearLayout
android:id="@+id/control_sets" android:id="@+id/control_sets"
@ -95,6 +97,7 @@
<FrameLayout <FrameLayout
android:id="@+id/comment_bar" android:id="@+id/comment_bar"
android:elevation="@dimen/elevation_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>

@ -2,9 +2,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:background="?attr/asContentBackground"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" /> <include layout="@layout/toolbar" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/asWindowBackground"/>
</LinearLayout> </LinearLayout>

@ -23,14 +23,16 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal"> android:orientation="horizontal"
android:background="?attr/asWindowBackground"
android:elevation="@dimen/elevation_task_list">
<FrameLayout <FrameLayout
android:id="@+id/task_list_body" android:layout_weight="100"
style="@style/task_list_container"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent">
android:layout_weight="100">
<include layout="@layout/task_list_body_standard" />
<include layout="@layout/task_list_body_empty" /> <include layout="@layout/task_list_body_empty" />
@ -40,7 +42,6 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<android.support.design.widget.FloatingActionButton <android.support.design.widget.FloatingActionButton

@ -7,6 +7,7 @@
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?selectableItemBackground" android:foreground="?selectableItemBackground"
app:cardBackgroundColor="?attr/asContentBackground"
app:cardCornerRadius="2dp"> app:cardCornerRadius="2dp">
<TextView <TextView

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rowBody"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingBottom="5dp"
android:paddingTop="5dp">
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:gravity="center"
android:paddingEnd="@dimen/keyline_second"
android:paddingLeft="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_second"
android:paddingStart="@dimen/keyline_first"
android:scaleType="center" />
<LinearLayout
android:id="@+id/task_row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/completeBox"
android:layout_toRightOf="@id/completeBox"
android:gravity="center_vertical"
android:orientation="vertical">
<!-- row 1 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:maxLines="1"
android:paddingEnd="@dimen/keyline_first"
android:paddingLeft="0dp"
android:paddingRight="@dimen/keyline_first"
android:paddingStart="0dp"
android:singleLine="true"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorPrimary" />
<LinearLayout
android:id="@+id/taskActionContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end|center_vertical"
android:orientation="vertical"
android:paddingEnd="@dimen/keyline_first"
android:paddingLeft="0dp"
android:paddingRight="@dimen/keyline_first"
android:paddingStart="0dp"
android:visibility="gone">
<ImageView
android:id="@+id/taskActionIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:alpha="?attr/alpha_secondary"
android:scaleType="center"
android:tint="?attr/icon_tint" />
</LinearLayout>
</LinearLayout>
<!-- row 2 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tag_block"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:ellipsize="none"
android:maxLines="1"
android:paddingEnd="@dimen/keyline_first"
android:paddingLeft="0dp"
android:paddingRight="@dimen/keyline_first"
android:paddingStart="0dp"
android:singleLine="true" />
<TextView
android:id="@+id/due_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/tag_block"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignStart="@id/tag_block"
android:ellipsize="end"
android:gravity="center_vertical"
android:paddingEnd="@dimen/keyline_first"
android:paddingLeft="0dp"
android:paddingRight="@dimen/keyline_first"
android:paddingStart="0dp"
android:singleLine="true" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

@ -1,133 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!-- <?xml version="1.0" encoding="utf-8"?>
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/row"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<RelativeLayout <include layout="@layout/task_adapter_row_body" />
android:id="@+id/rowBody"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dip"
android:layout_marginTop="5dip">
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:gravity="center"
android:paddingEnd="@dimen/keyline_second"
android:paddingLeft="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_second"
android:paddingStart="@dimen/keyline_first"
android:scaleType="center" />
<LinearLayout
android:id="@+id/task_row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/completeBox"
android:layout_toRightOf="@id/completeBox"
android:gravity="center_vertical"
android:orientation="vertical">
<!-- row 1 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:ellipsize="end"
android:textColor="?android:attr/textColorPrimary"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:maxLines="1"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingEnd="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_first"
android:singleLine="true" />
<LinearLayout
android:id="@+id/taskActionContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end|center_vertical"
android:orientation="vertical"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingEnd="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_first"
android:visibility="gone">
<ImageView
android:id="@+id/taskActionIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:alpha="?attr/alpha_secondary"
android:scaleType="center"
android:tint="?attr/icon_tint" />
</LinearLayout>
</LinearLayout>
<!-- row 2 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tag_block"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:ellipsize="none"
android:maxLines="1"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingEnd="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_first"
android:singleLine="true" />
<TextView
android:id="@+id/due_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignLeft="@id/tag_block"
android:layout_alignStart="@id/tag_block"
android:ellipsize="end"
android:gravity="center_vertical"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingEnd="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_first"
android:singleLine="true" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
<View style="@style/horizontal_divider" /> <View style="@style/horizontal_divider" />

@ -4,12 +4,13 @@
style="@style/task_list_container" style="@style/task_list_container"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:elevation="@dimen/elevation_task_list"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
<ScrollView <ScrollView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:elevation="@dimen/elevation_task_list"
android:background="?attr/asWindowBackground">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"

@ -1,16 +1,20 @@
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" <org.tasks.ui.DraggableSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_layout" android:id="@+id/swipe_layout"
style="@style/task_list_container" style="@style/task_list_container"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:elevation="@dimen/elevation_task_list"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
<ListView <android.support.v7.widget.RecyclerView
android:id="@android:id/list" android:id="@+id/recycler_view"
style="@style/TaskListView" android:layout_width="match_parent"
android:layout_width="fill_parent" android:layout_height="match_parent"
android:layout_height="fill_parent" /> android:scrollbars="vertical"
android:scrollbarStyle="outsideOverlay"
android:paddingBottom="@dimen/task_list_footer_height"
android:elevation="@dimen/elevation_task_list"
android:background="?attr/asWindowBackground"
android:clipToPadding="false"/>
</android.support.v4.widget.SwipeRefreshLayout> </org.tasks.ui.DraggableSwipeRefreshLayout>

@ -1,17 +0,0 @@
<org.tasks.ui.DraggableSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_layout"
style="@style/task_list_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:elevation="@dimen/elevation_task_list"
tools:ignore="UnusedAttribute">
<com.todoroo.astrid.ui.DraggableListView
android:id="@android:id/list"
style="@style/TaskListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"/>
</org.tasks.ui.DraggableSwipeRefreshLayout>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/task_list_footer_height"
android:orientation="vertical" />

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="background_color">@color/md_background_dark</color> <color name="window_background">@color/md_background_dark</color>
<color name="background_color_dialog">@color/md_background_dark_dialog</color> <color name="content_background">@color/grey_800</color>
<color name="dialog_background">@color/grey_800</color>
<color name="icon_tint">@color/icon_tint_dark</color> <color name="icon_tint">@color/icon_tint_dark</color>
<color name="drawer_color">@color/drawer_background_dark</color> <color name="drawer_color">@color/drawer_background_dark</color>
<color name="drawer_color_selected">@color/drawer_background_dark_selected</color> <color name="drawer_color_selected">@color/drawer_background_dark_selected</color>

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="background_color">@color/md_background_light</color> <color name="window_background">@color/grey_50</color>
<color name="background_color_dialog">@color/md_background_light_dialog</color> <color name="content_background">@android:color/white</color>
<color name="dialog_background">@android:color/white</color>
<color name="icon_tint">@color/icon_tint_light</color> <color name="icon_tint">@color/icon_tint_light</color>
<color name="drawer_color">@color/drawer_background_light</color> <color name="drawer_color">@color/drawer_background_light</color>
<color name="drawer_color_selected">@color/drawer_background_light_selected</color> <color name="drawer_color_selected">@color/drawer_background_light_selected</color>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="task_list_container"> <style name="task_list_container">
<item name="android:background">?attr/asContentBackground</item> <item name="android:elevation">@dimen/elevation_refresh_indicator</item>
<item name="android:clipToPadding">false</item> <item name="android:clipToPadding">false</item>
<item name="android:paddingLeft">0dp</item> <item name="android:paddingLeft">0dp</item>
<item name="android:paddingStart" tools:ignore="NewApi">0dp</item> <item name="android:paddingStart" tools:ignore="NewApi">0dp</item>

@ -6,6 +6,7 @@
<resources> <resources>
<!-- theme attributes --> <!-- theme attributes -->
<attr name="asWindowBackground" format="color" />
<attr name="asContentBackground" format="color" /> <attr name="asContentBackground" format="color" />
<attr name="asTextColor" format="color" /> <attr name="asTextColor" format="color" />
<attr name="asTextColorHint" format="color" /> <attr name="asTextColorHint" format="color" />
@ -18,6 +19,7 @@
<attr name="alpha_disabled" format="dimension" /> <attr name="alpha_disabled" format="dimension" />
<attr name="fab_text" format="color" /> <attr name="fab_text" format="color" />
<attr name="colorAccentDialog" format="color" /> <attr name="colorAccentDialog" format="color" />
<attr name="card_elevation" format="dimension" />
<declare-styleable name="TimePreference"> <declare-styleable name="TimePreference">
<attr name="summary" format="string" /> <attr name="summary" format="string" />

@ -73,10 +73,12 @@
<color name="brown_500">#795548</color> <color name="brown_500">#795548</color>
<color name="brown_700">#5d4037</color> <color name="brown_700">#5d4037</color>
<color name="grey_50">#fafafa</color>
<color name="grey_500">#9e9e9e</color> <color name="grey_500">#9e9e9e</color>
<color name="grey_700">#616161</color> <color name="grey_700">#616161</color>
<color name="grey_800">#424242</color> <color name="grey_800">#424242</color>
<color name="grey_900">#212121</color> <color name="grey_900">#212121</color>
<color name="grey_statusbar">#111111</color>
<color name="blue_grey_400">#78909c</color> <color name="blue_grey_400">#78909c</color>
<color name="blue_grey_500">#607d8b</color> <color name="blue_grey_500">#607d8b</color>
@ -104,10 +106,7 @@
<color name="white_50">#80ffffff</color> <color name="white_50">#80ffffff</color>
<color name="white_12">#1fffffff</color> <color name="white_12">#1fffffff</color>
<color name="md_background_light">#fafafa</color>
<color name="md_background_light_dialog">#ffffff</color>
<color name="md_background_dark">#303030</color> <color name="md_background_dark">#303030</color>
<color name="md_background_dark_dialog">#424242</color>
<color name="widget_background_black">#000000</color> <color name="widget_background_black">#000000</color>
<color name="icon_tint_light">@android:color/black</color> <color name="icon_tint_light">@android:color/black</color>
<color name="icon_tint_dark">@android:color/white</color> <color name="icon_tint_dark">@android:color/white</color>

@ -18,7 +18,8 @@
<item name="alpha_disabled" format="float" type="dimen">0.38</item> <item name="alpha_disabled" format="float" type="dimen">0.38</item>
<dimen name="elevation_toolbar">8dp</dimen> <dimen name="elevation_toolbar">8dp</dimen>
<dimen name="elevation_task_list">6dp</dimen> <dimen name="elevation_task_list">2dp</dimen>
<dimen name="elevation_refresh_indicator">3dp</dimen>
<dimen name="elevation_padding">0dp</dimen> <dimen name="elevation_padding">0dp</dimen>
<dimen name="widget_padding">10dp</dimen> <dimen name="widget_padding">10dp</dimen>

@ -113,7 +113,7 @@
</style> </style>
<style name="task_list_container"> <style name="task_list_container">
<item name="android:background">?attr/asContentBackground</item> <item name="android:elevation">@dimen/elevation_refresh_indicator</item>
</style> </style>
<style name="TextAppearance.Medium"> <style name="TextAppearance.Medium">

@ -4,7 +4,8 @@
<style name="Tasks" parent="Theme.AppCompat.DayNight.NoActionBar"> <style name="Tasks" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowBackground">@null</item> <item name="android:windowBackground">@null</item>
<item name="windowActionModeOverlay">true</item> <item name="windowActionModeOverlay">true</item>
<item name="asContentBackground">@color/background_color</item> <item name="asWindowBackground">@color/window_background</item>
<item name="asContentBackground">@color/content_background</item>
<item name="asTextColor">@color/text_primary</item> <item name="asTextColor">@color/text_primary</item>
<item name="asTextColorHint">@color/text_tertiary</item> <item name="asTextColorHint">@color/text_tertiary</item>
<item name="icon_tint">@color/icon_tint</item> <item name="icon_tint">@color/icon_tint</item>
@ -22,32 +23,36 @@
<item name="nnf_separator_color">@color/nnf_light_separator_color</item> <item name="nnf_separator_color">@color/nnf_light_separator_color</item>
<item name="nnf_save_icon_color">?attr/colorAccent</item> <item name="nnf_save_icon_color">?attr/colorAccent</item>
<item name="nnf_dir_icon_color">?attr/colorAccent</item> <item name="nnf_dir_icon_color">?attr/colorAccent</item>
<item name="card_elevation">2dp</item>
</style> </style>
<style name="TasksOverride" parent="Tasks"> <style name="TasksOverride" parent="Tasks">
</style> </style>
<style name="TasksDialogAlert" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert"> <style name="TasksDialogAlert" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert">
<item name="android:windowBackground">@color/background_color_dialog</item> <item name="android:windowBackground">@color/dialog_background</item>
<item name="colorAccent">?attr/colorAccentDialog</item> <item name="colorAccent">?attr/colorAccentDialog</item>
<item name="asTextColor">@color/text_primary</item> <item name="asTextColor">@color/text_primary</item>
<item name="icon_tint">@color/icon_tint</item> <item name="icon_tint">@color/icon_tint</item>
</style> </style>
<style name="TasksDialog" parent="@style/Theme.AppCompat.DayNight.Dialog"> <style name="TasksDialog" parent="@style/Theme.AppCompat.DayNight.Dialog">
<item name="android:windowBackground">@color/background_color_dialog</item> <item name="android:windowBackground">@color/dialog_background</item>
<item name="colorAccent">?attr/colorAccentDialog</item> <item name="colorAccent">?attr/colorAccentDialog</item>
<item name="asTextColor">@color/text_primary</item> <item name="asTextColor">@color/text_primary</item>
<item name="icon_tint">@color/icon_tint</item> <item name="icon_tint">@color/icon_tint</item>
</style> </style>
<style name="ThemeBlack" parent="TasksOverride"> <style name="ThemeBlack" parent="TasksOverride">
<item name="asWindowBackground">@color/grey_900</item>
<item name="asContentBackground">@android:color/black</item> <item name="asContentBackground">@android:color/black</item>
</style> </style>
<style name="Wallpaper" parent="TasksOverride"> <style name="Wallpaper" parent="TasksOverride">
<item name="asContentBackground">@color/black_38</item> <item name="asWindowBackground">@color/black_38</item>
<item name="asContentBackground">@color/black_54</item>
<item name="android:windowShowWallpaper">true</item> <item name="android:windowShowWallpaper">true</item>
<item name="card_elevation">0dp</item>
</style> </style>
</resources> </resources>

@ -2,7 +2,7 @@
<resources> <resources>
<style name="DarkGrey" parent="WhiteToolbarTheme"> <style name="DarkGrey" parent="WhiteToolbarTheme">
<item name="colorPrimary">@color/grey_900</item> <item name="colorPrimary">@color/grey_900</item>
<item name="colorPrimaryDark">#111111</item> <item name="colorPrimaryDark">@color/grey_statusbar</item>
</style> </style>
</resources> </resources>
Loading…
Cancel
Save