mirror of https://github.com/tasks/tasks
Fix action mode memory leak
parent
48a218e63d
commit
72101b2ebb
@ -1,147 +0,0 @@
|
||||
package org.tasks.tasklist;
|
||||
|
||||
import static com.todoroo.astrid.activity.TaskListFragment.REQUEST_MOVE_TASKS;
|
||||
import static org.tasks.activities.RemoteListSupportPicker.newRemoteListSupportPicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import com.todoroo.astrid.activity.MainActivity;
|
||||
import com.todoroo.astrid.activity.TaskListFragment;
|
||||
import com.todoroo.astrid.adapter.TaskAdapter;
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.service.TaskDeleter;
|
||||
import com.todoroo.astrid.service.TaskDuplicator;
|
||||
import com.todoroo.astrid.service.TaskMover;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.tasks.R;
|
||||
import org.tasks.analytics.Tracker;
|
||||
import org.tasks.analytics.Tracking;
|
||||
import org.tasks.dialogs.DialogBuilder;
|
||||
import org.tasks.injection.ForActivity;
|
||||
import org.tasks.ui.MenuColorizer;
|
||||
|
||||
public class ActionModeProvider {
|
||||
|
||||
private static final String FRAG_TAG_REMOTE_LIST_PICKER = "frag_tag_remote_list_picker";
|
||||
|
||||
private final Context context;
|
||||
private final DialogBuilder dialogBuilder;
|
||||
private final TaskDeleter taskDeleter;
|
||||
private final TaskDuplicator taskDuplicator;
|
||||
private final TaskMover taskMover;
|
||||
private final Tracker tracker;
|
||||
|
||||
@Inject
|
||||
public ActionModeProvider(
|
||||
@ForActivity Context context,
|
||||
DialogBuilder dialogBuilder,
|
||||
TaskDeleter taskDeleter,
|
||||
TaskDuplicator taskDuplicator,
|
||||
TaskMover taskMover,
|
||||
Tracker tracker) {
|
||||
this.context = context;
|
||||
this.dialogBuilder = dialogBuilder;
|
||||
this.taskDeleter = taskDeleter;
|
||||
this.taskDuplicator = taskDuplicator;
|
||||
this.taskMover = taskMover;
|
||||
this.tracker = tracker;
|
||||
}
|
||||
|
||||
ActionMode startActionMode(
|
||||
TaskAdapter adapter,
|
||||
TaskListFragment taskList,
|
||||
TaskListRecyclerAdapter taskListRecyclerAdapter) {
|
||||
return ((MainActivity) context)
|
||||
.startSupportActionMode(
|
||||
new ActionMode.Callback() {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
MenuInflater inflater = actionMode.getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_multi_select, menu);
|
||||
MenuColorizer.colorMenu(context, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.move_tasks:
|
||||
Filter singleFilter = taskMover.getSingleFilter(adapter.getSelected());
|
||||
(singleFilter == null
|
||||
? newRemoteListSupportPicker(taskList, REQUEST_MOVE_TASKS)
|
||||
: newRemoteListSupportPicker(
|
||||
singleFilter, taskList, REQUEST_MOVE_TASKS))
|
||||
.show(taskList.getFragmentManager(), FRAG_TAG_REMOTE_LIST_PICKER);
|
||||
return true;
|
||||
case R.id.delete:
|
||||
dialogBuilder
|
||||
.newDialog(R.string.delete_selected_tasks)
|
||||
.setPositiveButton(
|
||||
android.R.string.ok, (dialogInterface, i) -> deleteSelectedItems())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
return true;
|
||||
case R.id.copy_tasks:
|
||||
dialogBuilder
|
||||
.newDialog(R.string.copy_selected_tasks)
|
||||
.setPositiveButton(
|
||||
android.R.string.ok, ((dialogInterface, i) -> copySelectedItems()))
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode actionMode) {
|
||||
taskListRecyclerAdapter.onDestroyActionMode();
|
||||
if (adapter.getNumSelected() > 0) {
|
||||
adapter.clearSelections();
|
||||
taskListRecyclerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteSelectedItems() {
|
||||
tracker.reportEvent(Tracking.Events.MULTISELECT_DELETE);
|
||||
List<Long> tasks = adapter.getSelected();
|
||||
taskListRecyclerAdapter.finishActionMode();
|
||||
List<Task> result = taskDeleter.markDeleted(tasks);
|
||||
for (Task task : result) {
|
||||
taskList.onTaskDelete(task);
|
||||
}
|
||||
taskList
|
||||
.makeSnackbar(
|
||||
context.getString(
|
||||
R.string.delete_multiple_tasks_confirmation,
|
||||
Integer.toString(result.size())))
|
||||
.show();
|
||||
}
|
||||
|
||||
private void copySelectedItems() {
|
||||
tracker.reportEvent(Tracking.Events.MULTISELECT_CLONE);
|
||||
List<Long> tasks = adapter.getSelected();
|
||||
taskListRecyclerAdapter.finishActionMode();
|
||||
List<Task> duplicates = taskDuplicator.duplicate(tasks);
|
||||
taskList.onTaskCreated(duplicates);
|
||||
taskList
|
||||
.makeSnackbar(
|
||||
context.getString(
|
||||
R.string.copy_multiple_tasks_confirmation,
|
||||
Integer.toString(duplicates.size())))
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
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;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
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;
|
||||
private final TaskListRecyclerAdapter recyclerAdapter;
|
||||
private final Runnable onClear;
|
||||
private int from = -1;
|
||||
private int to = -1;
|
||||
private boolean dragging;
|
||||
|
||||
ItemTouchHelperCallback(
|
||||
TaskAdapter adapter, TaskListRecyclerAdapter recyclerAdapter, Runnable onClear) {
|
||||
this.adapter = adapter;
|
||||
this.recyclerAdapter = recyclerAdapter;
|
||||
this.onClear = onClear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||
recyclerAdapter.startActionMode();
|
||||
((ViewHolder) viewHolder).setMoving(true);
|
||||
dragging = true;
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
updateIndents((ViewHolder) viewHolder, position, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(RecyclerView recyclerView, 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) {
|
||||
recyclerAdapter.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;
|
||||
recyclerAdapter.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 == recyclerAdapter.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(
|
||||
Canvas c,
|
||||
RecyclerView recyclerView,
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
ViewHolder vh = (ViewHolder) viewHolder;
|
||||
vh.setMoving(false);
|
||||
dragging = 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++;
|
||||
}
|
||||
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;
|
||||
to = -1;
|
||||
Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
boolean isDragging() {
|
||||
return dragging;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue