diff --git a/app/src/main/java/com/todoroo/astrid/adapter/AstridTaskAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/AstridTaskAdapter.java index cab923d9a..62f49c1c3 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/AstridTaskAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/AstridTaskAdapter.java @@ -43,7 +43,6 @@ public final class AstridTaskAdapter extends TaskAdapter { return true; } - @Override public boolean canIndent(int position, TaskContainer task) { String parentUuid = getItemUuid(position - 1); int parentIndent = updater.getIndentForTask(parentUuid); @@ -56,7 +55,7 @@ public final class AstridTaskAdapter extends TaskAdapter { } @Override - public void moved(int from, int to) { + public void moved(int from, int to, int indent) { String targetTaskId = getItemUuid(from); if (!Task.isValidUuid(targetTaskId)) { return; // This can happen with gestures on empty parts of the list (e.g. extra space below @@ -76,7 +75,7 @@ public final class AstridTaskAdapter extends TaskAdapter { } @Override - public void indented(int which, int delta) { + public void swiped(int which, int delta) { String targetTaskId = getItemUuid(which); if (!Task.isValidUuid(targetTaskId)) { return; // This can happen with gestures on empty parts of the list (e.g. extra space below diff --git a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java index acf0f5b18..86cf04014 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java @@ -23,7 +23,7 @@ public final class GoogleTaskAdapter extends TaskAdapter { @Override public int getIndent(TaskContainer task) { - return task.getParent() > 0 ? 1 : 0; + return task.getIndent(); } @Override @@ -56,8 +56,13 @@ public final class GoogleTaskAdapter extends TaskAdapter { } @Override - public boolean canIndent(int position, TaskContainer task) { - return position > 0 && !task.hasChildren() && !task.hasParent(); + public int maxIndent(int position, TaskContainer task) { + return position == 0 || task.hasChildren() ? 0 : 1; + } + + @Override + public int minIndent(int nextPosition, TaskContainer task) { + return task.hasChildren() || !getTask(nextPosition).hasParent() ? 0 : 1; } @Override @@ -66,51 +71,36 @@ public final class GoogleTaskAdapter extends TaskAdapter { } @Override - public void moved(int from, int to) { + public void moved(int from, int to, int indent) { TaskContainer task = getTask(from); GoogleTask googleTask = task.getGoogleTask(); - if (to == 0) { + TaskContainer previous = to > 0 ? getTask(to - 1) : null; + + if (previous == null) { googleTaskDao.move(googleTask, 0, 0); - } else if (to == getCount()) { - TaskContainer previous = getTask(to - 1); - if (googleTask.getParent() > 0 && googleTask.getParent() == previous.getParent()) { - googleTaskDao.move(googleTask, googleTask.getParent(), previous.getSecondarySort()); + } else if (to == getCount() || to <= from) { + if (indent == 0) { + googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + 1); + } else if (previous.hasParent()) { + googleTaskDao.move(googleTask, previous.getParent(), previous.getSecondarySort() + 1); } else { - googleTaskDao.move(googleTask, 0, previous.getPrimarySort()); - } - } else if (from < to) { - TaskContainer previous = getTask(to - 1); - TaskContainer next = getTask(to); - if (previous.hasParent()) { - if (next.hasParent()) { - googleTaskDao.move(googleTask, next.getParent(), next.getSecondarySort()); - } else if (task.getParent() == previous.getParent() || next.hasParent()) { - googleTaskDao.move(googleTask, previous.getParent(), previous.getSecondarySort()); - } else { - googleTaskDao.move(googleTask, 0, previous.getPrimarySort()); - } - } else if (previous.hasChildren()) { googleTaskDao.move(googleTask, previous.getId(), 0); - } else if (task.hasParent()) { - googleTaskDao.move(googleTask, 0, next.getPrimarySort()); - } else { - googleTaskDao.move(googleTask, 0, previous.getPrimarySort()); } } else { - TaskContainer previous = getTask(to - 1); - TaskContainer next = getTask(to); - if (previous.hasParent()) { - if (next.hasParent()) { - googleTaskDao.move(googleTask, next.getParent(), next.getSecondarySort()); - } else if (task.getParent() == previous.getParent()) { - googleTaskDao.move(googleTask, previous.getParent(), previous.getSecondarySort()); - } else { - googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + 1); - } - } else if (previous.hasChildren()) { - googleTaskDao.move(googleTask, previous.getId(), 0); + if (indent == 0) { + googleTaskDao.move( + googleTask, + 0, + task.hasParent() ? previous.getPrimarySort() + 1 : previous.getPrimarySort()); + } else if (previous.hasParent()) { + googleTaskDao.move( + googleTask, + previous.getParent(), + task.getParent() == previous.getParent() + ? previous.getSecondarySort() + : previous.getSecondarySort() + 1); } else { - googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + 1); + googleTaskDao.move(googleTask, previous.getId(), 0); } } @@ -125,24 +115,6 @@ public final class GoogleTaskAdapter extends TaskAdapter { } @Override - public void indented(int which, int delta) { - TaskContainer task = getTask(which); - TaskContainer previous; - GoogleTask current = task.getGoogleTask(); - if (delta == -1) { - googleTaskDao.unindent(googleTaskDao.getByTaskId(task.getParent()), current); - } else { - previous = getTask(which - 1); - googleTaskDao.indent(googleTaskDao.getByTaskId(previous.getId()), current); - } - - Task update = task.getTask(); - update.setModificationDate(now()); - update.putTransitory(SyncFlags.FORCE_SYNC, true); - taskDao.save(update); - - if (BuildConfig.DEBUG) { - googleTaskDao.validateSorting(task.getGoogleTaskList()); - } + public void swiped(int which, int delta) { } } diff --git a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java index 15aa91029..c371ffed7 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java @@ -60,8 +60,12 @@ public class TaskAdapter { return false; } - public boolean canIndent(int position, TaskContainer task) { - return false; + public int maxIndent(int position, TaskContainer task) { + return 0; + } + + public int minIndent(int nextPosition, TaskContainer task) { + return 0; } public boolean isSelected(TaskContainer task) { @@ -81,9 +85,9 @@ public class TaskAdapter { return false; } - public void moved(int from, int to) {} + public void moved(int from, int to, int indent) {} - public void indented(int position, int delta) {} + public void swiped(int position, int delta) {} public TaskContainer getTask(int position) { return helper.getItem(position); diff --git a/app/src/main/java/org/tasks/data/GoogleTaskDao.java b/app/src/main/java/org/tasks/data/GoogleTaskDao.java index 3ae8fd933..ffefa22fc 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskDao.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskDao.java @@ -43,32 +43,6 @@ public abstract class GoogleTaskDao { "UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position") abstract void shiftUp(String listId, long parent, long position); - @Transaction - public void unindent(GoogleTask parent, GoogleTask task) { - String list = parent.getListId(); - shiftUp(list, task.getParent(), task.getOrder()); - long newPosition = parent.getOrder() + 1; - shiftDown(list, 0, newPosition); - task.setParent(0); - task.setOrder(newPosition); - task.setMoved(true); - update(task); - } - - @Transaction - public void indent(GoogleTask previous, GoogleTask task) { - shiftUp(previous.getListId(), 0, task.getOrder()); - if (previous.getParent() == 0) { - task.setParent(previous.getTask()); - task.setOrder(0); - } else { - task.setParent(previous.getParent()); - task.setOrder(previous.getOrder() + 1); - } - task.setMoved(true); - update(task); - } - @Transaction public void move(GoogleTask task, long newParent, long newPosition) { long previousParent = task.getParent(); diff --git a/app/src/main/java/org/tasks/data/TaskContainer.java b/app/src/main/java/org/tasks/data/TaskContainer.java index 8961ff0a2..66d734187 100644 --- a/app/src/main/java/org/tasks/data/TaskContainer.java +++ b/app/src/main/java/org/tasks/data/TaskContainer.java @@ -13,6 +13,7 @@ public class TaskContainer { public long primarySort; public long secondarySort; @Deprecated public int indent; + private int targetIndent; public String getTagsString() { return tags; @@ -83,11 +84,15 @@ public class TaskContainer { } public int getIndent() { + if (googletask != null) { + return getParent() > 0 ? 1 : 0; + } return indent; } public void setIndent(int indent) { this.indent = indent; + targetIndent = indent; } @Override @@ -113,6 +118,12 @@ public class TaskContainer { if (secondarySort != that.secondarySort) { return false; } + if (indent != that.indent) { + return false; + } + if (targetIndent != that.targetIndent) { + return false; + } if (task != null ? !task.equals(that.task) : that.task != null) { return false; } @@ -135,6 +146,8 @@ public class TaskContainer { result = 31 * result + siblings; result = 31 * result + (int) (primarySort ^ (primarySort >>> 32)); result = 31 * result + (int) (secondarySort ^ (secondarySort >>> 32)); + result = 31 * result + indent; + result = 31 * result + targetIndent; return result; } @@ -161,6 +174,8 @@ public class TaskContainer { + secondarySort + ", indent=" + indent + + ", targetIndent=" + + targetIndent + '}'; } @@ -191,4 +206,12 @@ public class TaskContainer { public GoogleTask getGoogleTask() { return googletask; } + + public void setTargetIndent(int indent) { + targetIndent = indent; + } + + public int getTargetIndent() { + return targetIndent; + } } diff --git a/app/src/main/java/org/tasks/tasklist/ItemTouchHelperCallback.java b/app/src/main/java/org/tasks/tasklist/ItemTouchHelperCallback.java index 6ccee6878..cb43246de 100644 --- a/app/src/main/java/org/tasks/tasklist/ItemTouchHelperCallback.java +++ b/app/src/main/java/org/tasks/tasklist/ItemTouchHelperCallback.java @@ -1,6 +1,8 @@ package org.tasks.tasklist; import static androidx.recyclerview.widget.ItemTouchHelper.DOWN; +import static androidx.recyclerview.widget.ItemTouchHelper.LEFT; +import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT; import static androidx.recyclerview.widget.ItemTouchHelper.UP; import android.graphics.Canvas; @@ -10,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.utility.Flags; +import org.tasks.data.TaskContainer; public class ItemTouchHelperCallback extends ItemTouchHelper.Callback { private final TaskAdapter adapter; @@ -19,6 +22,7 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback { private int from = -1; private int to = -1; private boolean dragging; + private boolean swiping; ItemTouchHelperCallback( TaskAdapter adapter, @@ -38,51 +42,50 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback { recyclerAdapter.startActionMode(); ((ViewHolder) viewHolder).setMoving(true); dragging = true; + int position = viewHolder.getAdapterPosition(); + updateIndents((ViewHolder) viewHolder, position, position); + } else if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + ((ViewHolder) viewHolder).setSwiping(true); + swiping = true; } } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return adapter.isManuallySorted() && adapter.getNumSelected() == 0 - ? makeMovementFlags(UP | DOWN, getSwipeFlags((ViewHolder) viewHolder)) + ? makeMovementFlags(UP | DOWN | LEFT | RIGHT, 0) : makeMovementFlags(0, 0); } - private int getSwipeFlags(ViewHolder vh) { - 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 indentFlags; - } - @Override public boolean onMove( @NonNull RecyclerView recyclerView, - @NonNull RecyclerView.ViewHolder source, + @NonNull RecyclerView.ViewHolder src, @NonNull RecyclerView.ViewHolder target) { recyclerAdapter.finishActionMode(); - int fromPosition = source.getAdapterPosition(); + int fromPosition = src.getAdapterPosition(); int toPosition = target.getAdapterPosition(); - if (!adapter.canMove((ViewHolder) source, (ViewHolder) target)) { + ViewHolder source = (ViewHolder) src; + if (!adapter.canMove(source, (ViewHolder) target)) { return false; } if (from == -1) { - ((ViewHolder) source).setSelected(false); + source.setSelected(false); from = fromPosition; } to = toPosition; recyclerAdapter.notifyItemMoved(fromPosition, toPosition); + updateIndents(source, from, to); return true; } - @Override - public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) { - return .2f; + private void updateIndents(ViewHolder source, int from, int to) { + TaskContainer task = source.task; + source.setMinIndent( + to == 0 || to == recyclerAdapter.getItemCount() - 1 + ? 0 + : adapter.minIndent(from <= to ? to + 1 : to, task)); + source.setMaxIndent(adapter.maxIndent(to, task)); } @Override @@ -94,9 +97,39 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback { 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)); + ViewHolder vh = (ViewHolder) viewHolder; + TaskContainer task = vh.task; + float shiftSize = vh.getShiftSize(); + if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { + int currentIndent = ((ViewHolder) viewHolder).getIndent(); + int maxIndent = vh.getMaxIndent(); + int minIndent = vh.getMinIndent(); + if (isCurrentlyActive) { + float dxAdjusted; + if (dX > 0) { + dxAdjusted = Math.min(dX, (maxIndent - currentIndent) * shiftSize); + } else { + dxAdjusted = Math.max((currentIndent - minIndent) * -shiftSize, dX); + } + + int targetIndent = currentIndent + Float.valueOf(dxAdjusted / shiftSize).intValue(); + + if (targetIndent != task.getIndent()) { + if (from == -1) { + recyclerAdapter.finishActionMode(); + vh.setSelected(false); + } + } + if (targetIndent < minIndent) { + task.setTargetIndent(minIndent); + } else if (targetIndent > maxIndent) { + task.setTargetIndent(maxIndent); + } else { + task.setTargetIndent(targetIndent); + } + } + + dX = (task.getTargetIndent() - task.getIndent()) * shiftSize; } super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } @@ -106,17 +139,27 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback { super.clearView(recyclerView, viewHolder); ViewHolder vh = (ViewHolder) viewHolder; vh.setMoving(false); - onClear.run(); + vh.setSwiping(false); dragging = false; + swiping = false; + onClear.run(); if (recyclerAdapter.isActionModeActive()) { recyclerAdapter.toggle(vh); } else { + TaskContainer task = vh.task; + int targetIndent = task.getTargetIndent(); if (from >= 0 && from != to) { if (from < to) { to++; } - recyclerAdapter.moved(from, to); - taskList.loadTaskListContent(); + vh.task.setIndent(targetIndent); + vh.setIndent(targetIndent); + recyclerAdapter.moved(from, to, targetIndent); + } else if (task.getIndent() != targetIndent) { + int position = vh.getAdapterPosition(); + vh.task.setIndent(targetIndent); + vh.setIndent(targetIndent); + recyclerAdapter.moved(position, position, targetIndent); } } from = -1; @@ -126,11 +169,20 @@ public class ItemTouchHelperCallback extends ItemTouchHelper.Callback { @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - adapter.indented(viewHolder.getAdapterPosition(), direction == ItemTouchHelper.RIGHT ? 1 : -1); + ViewHolder vh = (ViewHolder) viewHolder; + vh.setSwiping(false); + swiping = false; + int delta = direction == ItemTouchHelper.RIGHT ? 1 : -1; + int position = viewHolder.getAdapterPosition(); + recyclerAdapter.swiped(position, delta); taskList.loadTaskListContent(); } - public boolean isDragging() { + boolean isDragging() { return dragging; } + + boolean isSwiping() { + return swiping; + } } diff --git a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java index c21164663..7ba71658b 100644 --- a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java +++ b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java @@ -99,7 +99,7 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter updates.add(update); - if (!itemTouchHelperCallback.isDragging()) { + if (!itemTouchHelperCallback.isDragging() && !itemTouchHelperCallback.isSwiping()) { drainQueue(); } } @@ -258,9 +258,13 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter notifyItemRangeChanged(position, count, payload); } - void moved(int from, int to) { - adapter.moved(from, to); + void moved(int from, int to, int indent) { + adapter.moved(from, to, indent); TaskContainer task = list.remove(from); list.add(from < to ? to - 1 : to, task); } + + void swiped(int position, int delta) { + adapter.swiped(position, delta); + } } diff --git a/app/src/main/java/org/tasks/tasklist/ViewHolder.java b/app/src/main/java/org/tasks/tasklist/ViewHolder.java index 56d02dd78..b80b98d61 100644 --- a/app/src/main/java/org/tasks/tasklist/ViewHolder.java +++ b/app/src/main/java/org/tasks/tasklist/ViewHolder.java @@ -83,6 +83,9 @@ public class ViewHolder extends RecyclerView.ViewHolder { private boolean selected; private boolean moving; private boolean isGoogleTaskList; + private boolean swiping; + private int minIndent; + private int maxIndent; ViewHolder( Activity context, @@ -200,10 +203,6 @@ public class ViewHolder extends RecyclerView.ViewHolder { return Math.round(indent * getShiftSize()); } - boolean isIndented() { - return indent > 0; - } - void bindView(TaskContainer task, boolean isGoogleTaskList) { this.task = task; this.isGoogleTaskList = isGoogleTaskList; @@ -328,6 +327,40 @@ public class ViewHolder extends RecyclerView.ViewHolder { setTaskAppearance(); } + public int getIndent() { + return indent; + } + + void setSwiping(boolean swiping) { + this.swiping = swiping; + } + + boolean isSwiping() { + return swiping; + } + + void setMinIndent(int minIndent) { + this.minIndent = minIndent; + if (task.getTargetIndent() < minIndent) { + task.setTargetIndent(minIndent); + } + } + + void setMaxIndent(int maxIndent) { + this.maxIndent = maxIndent; + if (task.getTargetIndent() > maxIndent) { + task.setTargetIndent(maxIndent); + } + } + + int getMinIndent() { + return minIndent; + } + + int getMaxIndent() { + return maxIndent; + } + interface ViewHolderCallbacks { void onCompletedTask(TaskContainer task, boolean newState);