mirror of https://github.com/tasks/tasks
Use paged list when not using drag and drop
parent
21a1b9cfc7
commit
40a542b945
@ -0,0 +1,269 @@
|
|||||||
|
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 static com.todoroo.andlib.utility.AndroidUtilities.assertMainThread;
|
||||||
|
import static com.todoroo.andlib.utility.AndroidUtilities.assertNotMainThread;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.util.Pair;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil.DiffResult;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.ListUpdateCallback;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.todoroo.astrid.activity.TaskListFragment;
|
||||||
|
import com.todoroo.astrid.adapter.TaskAdapter;
|
||||||
|
import com.todoroo.astrid.dao.TaskDao;
|
||||||
|
import com.todoroo.astrid.utility.Flags;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import io.reactivex.subjects.PublishSubject;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import org.tasks.data.TaskContainer;
|
||||||
|
|
||||||
|
public class DragAndDropRecyclerAdapter extends TaskListRecyclerAdapter {
|
||||||
|
|
||||||
|
private static final int LONG_LIST_SIZE = 500;
|
||||||
|
|
||||||
|
private final TaskAdapter adapter;
|
||||||
|
private final TaskListFragment taskList;
|
||||||
|
private final RecyclerView recyclerView;
|
||||||
|
private List<TaskContainer> list;
|
||||||
|
private PublishSubject<List<TaskContainer>> publishSubject = PublishSubject.create();
|
||||||
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
private Queue<Pair<List<TaskContainer>, DiffResult>> updates = new LinkedList<>();
|
||||||
|
private boolean dragging;
|
||||||
|
|
||||||
|
public DragAndDropRecyclerAdapter(
|
||||||
|
TaskAdapter adapter,
|
||||||
|
RecyclerView recyclerView,
|
||||||
|
ViewHolderFactory viewHolderFactory,
|
||||||
|
TaskListFragment taskList,
|
||||||
|
List<TaskContainer> list,
|
||||||
|
TaskDao taskDao) {
|
||||||
|
super(adapter, viewHolderFactory, taskList, taskDao);
|
||||||
|
|
||||||
|
this.adapter = adapter;
|
||||||
|
this.recyclerView = recyclerView;
|
||||||
|
this.taskList = taskList;
|
||||||
|
this.list = list;
|
||||||
|
new ItemTouchHelper(new ItemTouchHelperCallback()).attachToRecyclerView(recyclerView);
|
||||||
|
Pair<List<TaskContainer>, DiffResult> initial = Pair.create(list, null);
|
||||||
|
disposables.add(
|
||||||
|
publishSubject
|
||||||
|
.observeOn(Schedulers.computation())
|
||||||
|
.scan(initial, this::calculateDiff)
|
||||||
|
.skip(1)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(this::applyDiff));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskContainer getItem(int position) {
|
||||||
|
return list.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void submitList(List<TaskContainer> list) {
|
||||||
|
publishSubject.onNext(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<List<TaskContainer>, DiffResult> calculateDiff(
|
||||||
|
Pair<List<TaskContainer>, DiffResult> last, List<TaskContainer> next) {
|
||||||
|
assertNotMainThread();
|
||||||
|
|
||||||
|
DiffCallback cb = new DiffCallback(last.first, next, adapter);
|
||||||
|
DiffResult result = DiffUtil.calculateDiff(cb, next.size() < LONG_LIST_SIZE);
|
||||||
|
|
||||||
|
return Pair.create(next, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyDiff(Pair<List<TaskContainer>, DiffResult> update) {
|
||||||
|
assertMainThread();
|
||||||
|
|
||||||
|
updates.add(update);
|
||||||
|
|
||||||
|
if (!dragging) {
|
||||||
|
drainQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drainQueue() {
|
||||||
|
assertMainThread();
|
||||||
|
|
||||||
|
Parcelable recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
|
||||||
|
|
||||||
|
Pair<List<TaskContainer>, DiffResult> update = updates.poll();
|
||||||
|
while (update != null) {
|
||||||
|
list = update.first;
|
||||||
|
update.second.dispatchUpdatesTo((ListUpdateCallback) this);
|
||||||
|
update = updates.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||||
|
disposables.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||||
|
private int from = -1;
|
||||||
|
private int to = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||||
|
super.onSelectedChanged(viewHolder, actionState);
|
||||||
|
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||||
|
taskList.startActionMode();
|
||||||
|
((ViewHolder) viewHolder).setMoving(true);
|
||||||
|
dragging = true;
|
||||||
|
int position = viewHolder.getAdapterPosition();
|
||||||
|
updateIndents((ViewHolder) viewHolder, position, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMovementFlags(
|
||||||
|
@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||||
|
return adapter.supportsParentingOrManualSort() && adapter.getNumSelected() == 0
|
||||||
|
? makeMovementFlags(UP | DOWN | LEFT | RIGHT, 0)
|
||||||
|
: makeMovementFlags(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(
|
||||||
|
@NonNull RecyclerView recyclerView,
|
||||||
|
@NonNull RecyclerView.ViewHolder src,
|
||||||
|
@NonNull RecyclerView.ViewHolder target) {
|
||||||
|
taskList.finishActionMode();
|
||||||
|
int fromPosition = src.getAdapterPosition();
|
||||||
|
int toPosition = target.getAdapterPosition();
|
||||||
|
ViewHolder source = (ViewHolder) src;
|
||||||
|
if (!adapter.canMove(source, (ViewHolder) target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (from == -1) {
|
||||||
|
source.setSelected(false);
|
||||||
|
from = fromPosition;
|
||||||
|
}
|
||||||
|
to = toPosition;
|
||||||
|
notifyItemMoved(fromPosition, toPosition);
|
||||||
|
updateIndents(source, from, to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIndents(ViewHolder source, int from, int to) {
|
||||||
|
TaskContainer task = source.task;
|
||||||
|
source.setMinIndent(
|
||||||
|
to == 0 || to == getItemCount() - 1
|
||||||
|
? 0
|
||||||
|
: adapter.minIndent(from <= to ? to + 1 : to, task));
|
||||||
|
source.setMaxIndent(to == 0 ? 0 : adapter.maxIndent(from >= to ? to - 1 : to, task));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildDraw(
|
||||||
|
@NonNull Canvas c,
|
||||||
|
@NonNull RecyclerView recyclerView,
|
||||||
|
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||||
|
float dX,
|
||||||
|
float dY,
|
||||||
|
int actionState,
|
||||||
|
boolean isCurrentlyActive) {
|
||||||
|
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) {
|
||||||
|
taskList.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearView(
|
||||||
|
@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder);
|
||||||
|
ViewHolder vh = (ViewHolder) viewHolder;
|
||||||
|
vh.setMoving(false);
|
||||||
|
dragging = false;
|
||||||
|
drainQueue();
|
||||||
|
if (taskList.isActionModeActive()) {
|
||||||
|
toggle(vh);
|
||||||
|
} else {
|
||||||
|
TaskContainer task = vh.task;
|
||||||
|
int targetIndent = task.getTargetIndent();
|
||||||
|
if (from >= 0 && from != to) {
|
||||||
|
if (from < to) {
|
||||||
|
to++;
|
||||||
|
}
|
||||||
|
vh.task.setIndent(targetIndent);
|
||||||
|
vh.setIndent(targetIndent);
|
||||||
|
moved(from, to, targetIndent);
|
||||||
|
} else if (task.getIndent() != targetIndent) {
|
||||||
|
int position = vh.getAdapterPosition();
|
||||||
|
vh.task.setIndent(targetIndent);
|
||||||
|
vh.setIndent(targetIndent);
|
||||||
|
moved(position, position, targetIndent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
from = -1;
|
||||||
|
to = -1;
|
||||||
|
Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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);
|
||||||
|
taskList.loadTaskListContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package org.tasks.tasklist;
|
||||||
|
|
||||||
|
import androidx.paging.AsyncPagedListDiffer;
|
||||||
|
import androidx.paging.PagedList;
|
||||||
|
import androidx.recyclerview.widget.AsyncDifferConfig;
|
||||||
|
import com.todoroo.astrid.activity.TaskListFragment;
|
||||||
|
import com.todoroo.astrid.adapter.TaskAdapter;
|
||||||
|
import com.todoroo.astrid.dao.TaskDao;
|
||||||
|
import java.util.List;
|
||||||
|
import org.tasks.data.TaskContainer;
|
||||||
|
|
||||||
|
public class PagedListRecyclerAdapter extends TaskListRecyclerAdapter {
|
||||||
|
|
||||||
|
private AsyncPagedListDiffer<TaskContainer> differ;
|
||||||
|
|
||||||
|
public PagedListRecyclerAdapter(
|
||||||
|
TaskAdapter adapter,
|
||||||
|
ViewHolderFactory viewHolderFactory,
|
||||||
|
TaskListFragment taskList,
|
||||||
|
List<TaskContainer> list,
|
||||||
|
TaskDao taskDao) {
|
||||||
|
super(adapter, viewHolderFactory, taskList, taskDao);
|
||||||
|
differ =
|
||||||
|
new AsyncPagedListDiffer<>(
|
||||||
|
this, new AsyncDifferConfig.Builder<>(new ItemCallback()).build());
|
||||||
|
if (list instanceof PagedList) {
|
||||||
|
differ.submitList((PagedList<TaskContainer>) list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskContainer getItem(int position) {
|
||||||
|
return differ.getItem(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void submitList(List<TaskContainer> list) {
|
||||||
|
differ.submitList((PagedList<TaskContainer>) list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return differ.getItemCount();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue