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.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import com.todoroo.andlib.data.Property;
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.data.Task;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent;
import org.tasks.tasklist.GtasksListFragment;
@ -42,8 +39,6 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
@Inject Theme theme;
@Inject OrderedMetadataListFragmentHelper helper;
private int lastVisibleIndex = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -73,18 +68,6 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
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
public void setTaskAdapter() {
helper.setList(list);
@ -93,25 +76,9 @@ public class GtasksSubtaskListFragment extends GtasksListFragment {
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
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate());
return helper.createTaskAdapter(theme.wrap(context), cursor);
}
@Override

@ -5,14 +5,9 @@
*/
package com.todoroo.astrid.gtasks;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
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.utility.DateUtilities;
@ -23,20 +18,11 @@ import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
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.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
@ -44,9 +30,7 @@ import timber.log.Timber;
class OrderedMetadataListFragmentHelper {
private final DisplayMetrics metrics = new DisplayMetrics();
private final GtasksTaskListUpdater updater;
private final ViewHolderFactory viewHolderFactory;
private final TaskDao taskDao;
private final MetadataDao metadataDao;
@ -56,46 +40,52 @@ class OrderedMetadataListFragmentHelper {
private GtasksList list;
@Inject
OrderedMetadataListFragmentHelper(TaskDao taskDao, MetadataDao metadataDao,
GtasksTaskListUpdater updater, ViewHolderFactory viewHolderFactory) {
OrderedMetadataListFragmentHelper(TaskDao taskDao, MetadataDao metadataDao, GtasksTaskListUpdater updater) {
this.taskDao = taskDao;
this.metadataDao = metadataDao;
this.updater = updater;
this.viewHolderFactory = viewHolderFactory;
}
void setTaskListFragment(TaskListFragment fragment) {
this.fragment = fragment;
}
// --- ui component setup
private Activity getActivity() {
return fragment.getActivity();
void beforeSetUpTaskList(Filter filter) {
updater.initialize(filter);
}
private ListView getListView() {
return fragment.getListView();
}
TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor) {
taskAdapter = new DraggableTaskAdapter(context, fragment, cursor);
void setUpUiComponents() {
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);
}
taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
void beforeSetUpTaskList(Filter filter) {
updater.initialize(filter);
return taskAdapter;
}
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
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);
if (targetTaskId <= 0) {
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);
try {
if(to >= getListView().getCount()) {
if(to >= taskAdapter.getCount()) {
updater.moveTo(list, targetTaskId, -1);
} else {
updater.moveTo(list, targetTaskId, destinationTaskId);
@ -114,20 +104,9 @@ class OrderedMetadataListFragmentHelper {
fragment.loadTaskListContent();
}
};
private final SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
indent(which, 1);
}
@Override
public void swipeLeft(int which) {
indent(which, -1);
}
void indent(int which, int delta) {
public void indented(int which, int delta) {
long targetTaskId = taskAdapter.getItemId(which);
if (targetTaskId <= 0) {
return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
@ -139,51 +118,6 @@ class OrderedMetadataListFragmentHelper {
}
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 =

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

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

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

@ -7,7 +7,6 @@ package com.todoroo.astrid.adapter;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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.StringProperty;
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.api.PermaSql;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.tags.TaskToTagMetadata;
import org.tasks.R;
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
@ -41,7 +31,7 @@ import java.util.concurrent.atomic.AtomicReference;
* @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 {
void onCompletedTask(Task item, boolean newState);
@ -75,94 +65,55 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
// --- instance variables
private final TaskDao taskDao;
private final TaskListFragment fragment;
private OnCompletedTaskListener onCompletedTaskListener = null;
private final AtomicReference<String> query;
private final ViewHolderFactory viewHolderFactory;
public TaskAdapter(Context context, TaskDao taskDao, TaskListFragment fragment, Cursor c,
AtomicReference<String> query, ViewHolderFactory viewHolderFactory) {
public TaskAdapter(Context context, TaskListFragment fragment, Cursor c) {
super(context, c, false);
this.taskDao = taskDao;
this.query = query;
this.fragment = fragment;
this.viewHolderFactory = viewHolderFactory;
}
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (getFilterQueryProvider() != null) {
return getFilterQueryProvider().runQuery(constraint);
}
return fetchFiltered(query.get(), constraint, fragment.taskProperties());
public View newView(Context context, Cursor cursor, ViewGroup parent) {
throw new RuntimeException();
}
/**
* Fetch tasks for the given filter
* @param constraint text constraint, or null
*/
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;
}
@Override
public void bindView(View view, Context context, Cursor c) {
throw new RuntimeException();
}
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 View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewGroup view = (ViewGroup) LayoutInflater.from(context)
.inflate(R.layout.task_adapter_row_simple, parent, false);
public boolean isManuallySorted() {
return false;
}
// create view holder
viewHolderFactory.newViewHolder(view, this::onTaskCompleted);
public void moved(int from, int to) {
return view;
}
@Override
public void bindView(View view, Context context, Cursor c) {
TodorooCursor<Task> cursor = (TodorooCursor<Task>)c;
ViewHolder viewHolder = ((ViewHolder)view.getTag());
viewHolder.bindView(cursor);
public void indented(int position, int delta) {
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();
if (c != null) {
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
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
viewHolderFactory.updateTagMap();
public void onClick(long id) {
fragment.onTaskListItemClicked(id);
}
private void onTaskCompleted(Task task, boolean newState) {
@Override
public void onCompletedTask(Task task, boolean newState) {
if (onCompletedTaskListener != null) {
onCompletedTaskListener.onCompletedTask(task, newState);
}

@ -1,13 +1,8 @@
package com.todoroo.astrid.subtasks;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
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.sql.Criterion;
@ -21,20 +16,11 @@ import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
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.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
@ -42,58 +28,60 @@ import timber.log.Timber;
class AstridOrderedListFragmentHelper {
private final DisplayMetrics metrics = new DisplayMetrics();
private final SubtasksFilterUpdater updater;
private final TaskDao taskDao;
private final ViewHolderFactory viewHolderFactory;
private DraggableTaskAdapter taskAdapter;
private TaskListFragment fragment;
private TaskListMetadata list;
@Inject
AstridOrderedListFragmentHelper(SubtasksFilterUpdater updater, TaskDao taskDao, ViewHolderFactory viewHolderFactory) {
AstridOrderedListFragmentHelper(SubtasksFilterUpdater updater, TaskDao taskDao) {
this.updater = updater;
this.taskDao = taskDao;
this.viewHolderFactory = viewHolderFactory;
}
void setTaskListFragment(TaskListFragment fragment) {
this.fragment = fragment;
}
// --- ui component setup
private Activity getActivity() {
return fragment.getActivity();
void beforeSetUpTaskList(Filter filter) {
updater.initialize(list, filter);
}
private ListView getListView() {
return fragment.getListView();
}
TaskAdapter createTaskAdapter(Context context, TodorooCursor<Task> cursor) {
taskAdapter = new DraggableTaskAdapter(context, fragment, cursor);
private Filter getFilter() {
return fragment.getFilter();
}
taskAdapter.setOnCompletedTaskListener(this::setCompletedForItemAndSubtasks);
void setUpUiComponents() {
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);
return taskAdapter;
}
void beforeSetUpTaskList(Filter filter) {
updater.initialize(list, filter);
}
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 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
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);
if (!RemoteModel.isValidUuid(targetTaskId)) {
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);
try {
if(to >= getListView().getCount()) {
updater.moveTo(list, getFilter(), targetTaskId, "-1"); //$NON-NLS-1$
if(to >= taskAdapter.getCount()) {
updater.moveTo(list, fragment.getFilter(), targetTaskId, "-1"); //$NON-NLS-1$
} else {
updater.moveTo(list, getFilter(), targetTaskId, destinationTaskId);
updater.moveTo(list, fragment.getFilter(), targetTaskId, destinationTaskId);
}
} catch (Exception e) {
Timber.e(e, e.getMessage());
@ -113,26 +101,15 @@ class AstridOrderedListFragmentHelper {
fragment.reconstructCursor();
fragment.loadTaskListContent();
}
};
private final SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
indent(which, 1);
}
@Override
public void swipeLeft(int which) {
indent(which, -1);
}
void indent(int which, int delta) {
public void indented(int which, int delta) {
String targetTaskId = taskAdapter.getItemUuid(which);
if (!RemoteModel.isValidUuid(targetTaskId)) {
return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
}
try {
updater.indent(list, getFilter(), targetTaskId, delta);
updater.indent(list, fragment.getFilter(), targetTaskId, delta);
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
@ -140,51 +117,6 @@ class AstridOrderedListFragmentHelper {
fragment.reconstructCursor();
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 =
@ -250,13 +182,13 @@ class AstridOrderedListFragmentHelper {
}
void onCreateTask(String uuid) {
updater.onCreateTask(list, getFilter(), uuid);
updater.onCreateTask(list, fragment.getFilter(), uuid);
fragment.reconstructCursor();
fragment.loadTaskListContent();
}
void onDeleteTask(Task task) {
updater.onDeleteTask(list, getFilter(), task.getUuid());
updater.onDeleteTask(list, fragment.getFilter(), task.getUuid());
taskAdapter.notifyDataSetInvalidated();
}
}

@ -7,10 +7,7 @@ package com.todoroo.astrid.subtasks;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.ListView;
import com.todoroo.andlib.data.TodorooCursor;
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.TaskListMetadata;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent;
import org.tasks.preferences.Preferences;
@ -44,8 +40,6 @@ public class SubtasksListFragment extends TaskListFragment {
return fragment;
}
private int lastVisibleIndex = -1;
@Inject Preferences preferences;
@Inject @ForApplication Context context;
@Inject TaskListMetadataDao taskListMetadataDao;
@ -60,18 +54,6 @@ public class SubtasksListFragment extends TaskListFragment {
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
public void setTaskAdapter() {
helper.setList(initializeTaskListMetadata());
@ -108,22 +90,6 @@ public class SubtasksListFragment extends TaskListFragment {
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
public void onTaskCreated(String uuid) {
helper.onCreateTask(uuid);
@ -137,7 +103,7 @@ public class SubtasksListFragment extends TaskListFragment {
@Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate());
return helper.createTaskAdapter(theme.wrap(context), cursor);
}
@Override

@ -20,7 +20,6 @@ import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskListMetadata;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.injection.FragmentComponent;
import org.tasks.tasklist.TagListFragment;
@ -42,8 +41,6 @@ public class SubtasksTagListFragment extends TagListFragment {
@Inject Theme theme;
@Inject AstridOrderedListFragmentHelper helper;
private int lastVisibleIndex = -1;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@ -51,18 +48,6 @@ public class SubtasksTagListFragment extends TagListFragment {
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
public void setTaskAdapter() {
String tdId = tagData.getUuid();
@ -78,21 +63,6 @@ public class SubtasksTagListFragment extends TagListFragment {
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
public void onTaskCreated(String uuid) {
helper.onCreateTask(uuid);
@ -106,7 +76,7 @@ public class SubtasksTagListFragment extends TagListFragment {
@Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(theme.wrap(context), cursor, taskListDataProvider.getSqlQueryTemplate());
return helper.createTaskAdapter(theme.wrap(context), cursor);
}
@Override

@ -128,6 +128,10 @@ public final class TagService {
return tagList;
}
public TagData getTagByUuid(String uuid) {
return tagDataDao.getByUuid(uuid);
}
/**
* If a tag already exists in the database that case insensitively matches the
* 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();
}
});
toolbar.inflateMenu(R.menu.tag_settings_activity);
toolbar.inflateMenu(R.menu.menu_tag_settings);
toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(this, toolbar);

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

@ -39,10 +39,6 @@ public class TaskListDataProvider {
this.preferences = preferences;
}
public AtomicReference<String> getSqlQueryTemplate() {
return sqlQueryTemplate;
}
public TodorooCursor<Task> constructCursor(Filter filter, Property<?>[] properties) {
Criterion tagsJoinCriterion = Criterion.and(
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();
context.getResources().getValue(R.dimen.tag_characters, typedValue, true);
tagCharacters = typedValue.getFloat();
}
public void updateTagMap() {
tagMap.clear();
for (TagData tagData : tagService.getTagList()) {
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());
List<TagData> firstFourByName = orderByName.leastOf(t, 4);
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>() {
@Override

@ -96,13 +96,6 @@ public class TagListFragment extends TaskListFragment {
}
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnKeyListener(null);
}
@Override
public void onSaveInstanceState(Bundle 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.graphics.Paint;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
@ -37,25 +39,29 @@ import butterknife.ButterKnife;
import timber.log.Timber;
import static com.google.common.collect.Lists.newArrayList;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop;
/**
* View Holder saves a lot of findViewById lookups.
*
* @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 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.completeBox) public CheckableImageView completeBox;
@BindView(R.id.completeBox) CheckableImageView completeBox;
@BindView(R.id.due_date) public TextView dueDate;
@BindView(R.id.tag_block) TextView tagBlock;
@BindView(R.id.taskActionContainer) public View taskActionContainer;
@BindView(R.id.taskActionIcon) public ImageView taskActionIcon;
@BindView(R.id.taskActionContainer) View taskActionContainer;
@BindView(R.id.taskActionIcon) ImageView taskActionIcon;
public Task task;
@ -70,14 +76,18 @@ public class ViewHolder {
private final int textColorHint;
private final TaskDao taskDao;
private final DialogBuilder dialogBuilder;
private final OnCompletedTaskCallback callback;
private final ViewHolderCallbacks callback;
private final DisplayMetrics metrics;
private final int textColorOverdue;
private Pair<Float, Float> lastTouchYRawY = new Pair<>(0f, 0f);
private int indent;
public ViewHolder(Context context, ViewGroup view, boolean showFullTaskTitle, int fontSize,
CheckBoxes checkBoxes, TagFormatter tagFormatter,
int textColorOverdue, int textColorSecondary, int textColorHint, TaskDao taskDao,
DialogBuilder dialogBuilder, OnCompletedTaskCallback callback, int minRowHeight) {
DialogBuilder dialogBuilder, ViewHolderCallbacks callback, int minRowHeight,
DisplayMetrics metrics) {
super(view);
this.context = context;
this.fontSize = fontSize;
this.checkBoxes = checkBoxes;
@ -88,6 +98,7 @@ public class ViewHolder {
this.taskDao = taskDao;
this.dialogBuilder = dialogBuilder;
this.callback = callback;
this.metrics = metrics;
ButterKnife.bind(this, view);
task = new Task();
@ -108,7 +119,30 @@ public class ViewHolder {
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);
hasFiles = cursor.get(TaskAdapter.FILE_ID_PROPERTY) > 0;
hasNotes = cursor.get(TaskAdapter.HAS_NOTES_PROPERTY) > 0;
@ -120,7 +154,7 @@ public class ViewHolder {
setTaskAppearance();
}
public void setMinimumHeight(int minRowHeight) {
private void setMinimumHeight(int minRowHeight) {
if (fontSize < 16) {
rowBody.setMinimumHeight(0);
completeBox.setMinimumHeight(0);
@ -167,7 +201,7 @@ public class ViewHolder {
return LinkActionExposer.getActionsForTask(context, task, hasFiles, hasNotes);
}
public void setTaskAppearance() {
private void setTaskAppearance() {
boolean completed = task.isCompleted();
TextView name = nameView;
@ -258,6 +292,12 @@ public class ViewHolder {
completeBox.setOnTouchListener(otl);
completeBox.setOnClickListener(completeBoxListener);
rowBody.setOnClickListener(view -> {
if (!task.isDeleted()) {
callback.onClick(task.getId());
}
});
if (taskActionContainer != null) {
taskActionContainer.setOnClickListener(v -> {
TaskAction action = (TaskAction) taskActionIcon.getTag();

@ -30,6 +30,7 @@ public class ViewHolderFactory {
private final TaskDao taskDao;
private final DialogBuilder dialogBuilder;
private final int minRowHeight;
private final DisplayMetrics metrics;
@Inject
public ViewHolderFactory(@ForActivity Context context, Preferences preferences,
@ -45,18 +46,13 @@ public class ViewHolderFactory {
textColorOverdue = getColor(context, R.color.overdue);
showFullTaskTitle = preferences.getBoolean(R.string.p_fullTaskTitle, false);
fontSize = preferences.getIntegerFromString(R.string.p_fontSize, 18);
tagFormatter.updateTagMap();
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
metrics = context.getResources().getDisplayMetrics();
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,
tagFormatter, textColorOverdue, textColorSecondary, textColorHint, taskDao,
dialogBuilder, onCompletedTaskCallback, minRowHeight);
}
public void updateTagMap() {
tagFormatter.updateTagMap();
dialogBuilder, callbacks, minRowHeight, metrics);
}
}

@ -25,11 +25,11 @@ public class ThemeCache {
public ThemeCache(Context context) {
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_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_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);
for (int i = 0 ; i < ThemeColor.COLORS.length ; i++) {

@ -4,7 +4,7 @@ import org.tasks.R;
public class WidgetTheme {
public static final int[] BACKGROUNDS = new int[] {
R.color.md_background_light,
R.color.grey_50,
R.color.widget_background_black,
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?asContentBackground">
android:orientation="vertical">
<include layout="@layout/toolbar" />
<android.support.v7.widget.RecyclerView
android:id="@android:id/list"
android:background="?asWindowBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

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

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

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

@ -7,6 +7,7 @@
android:clickable="true"
android:focusable="true"
android:foreground="?selectableItemBackground"
app:cardBackgroundColor="?attr/asContentBackground"
app:cardCornerRadius="2dp">
<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"?><!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<?xml version="1.0" encoding="utf-8"?>
<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_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
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>
<include layout="@layout/task_adapter_row_body" />
<View style="@style/horizontal_divider" />

@ -4,12 +4,13 @@
style="@style/task_list_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:elevation="@dimen/elevation_task_list"
tools:ignore="UnusedAttribute">
<ScrollView
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
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"
android:id="@+id/swipe_layout"
style="@style/task_list_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:elevation="@dimen/elevation_task_list"
tools:ignore="UnusedAttribute">
<ListView
android:id="@android:id/list"
style="@style/TaskListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_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"?>
<resources>
<color name="background_color">@color/md_background_dark</color>
<color name="background_color_dialog">@color/md_background_dark_dialog</color>
<color name="window_background">@color/md_background_dark</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="drawer_color">@color/drawer_background_dark</color>
<color name="drawer_color_selected">@color/drawer_background_dark_selected</color>

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="background_color">@color/md_background_light</color>
<color name="background_color_dialog">@color/md_background_light_dialog</color>
<color name="window_background">@color/grey_50</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="drawer_color">@color/drawer_background_light</color>
<color name="drawer_color_selected">@color/drawer_background_light_selected</color>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<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:paddingLeft">0dp</item>
<item name="android:paddingStart" tools:ignore="NewApi">0dp</item>

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

@ -73,10 +73,12 @@
<color name="brown_500">#795548</color>
<color name="brown_700">#5d4037</color>
<color name="grey_50">#fafafa</color>
<color name="grey_500">#9e9e9e</color>
<color name="grey_700">#616161</color>
<color name="grey_800">#424242</color>
<color name="grey_900">#212121</color>
<color name="grey_statusbar">#111111</color>
<color name="blue_grey_400">#78909c</color>
<color name="blue_grey_500">#607d8b</color>
@ -104,10 +106,7 @@
<color name="white_50">#80ffffff</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_dialog">#424242</color>
<color name="widget_background_black">#000000</color>
<color name="icon_tint_light">@android:color/black</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>
<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="widget_padding">10dp</dimen>

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

@ -4,7 +4,8 @@
<style name="Tasks" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowBackground">@null</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="asTextColorHint">@color/text_tertiary</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_save_icon_color">?attr/colorAccent</item>
<item name="nnf_dir_icon_color">?attr/colorAccent</item>
<item name="card_elevation">2dp</item>
</style>
<style name="TasksOverride" parent="Tasks">
</style>
<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="asTextColor">@color/text_primary</item>
<item name="icon_tint">@color/icon_tint</item>
</style>
<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="asTextColor">@color/text_primary</item>
<item name="icon_tint">@color/icon_tint</item>
</style>
<style name="ThemeBlack" parent="TasksOverride">
<item name="asWindowBackground">@color/grey_900</item>
<item name="asContentBackground">@android:color/black</item>
</style>
<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="card_elevation">0dp</item>
</style>
</resources>

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