From 410af772f27d7ba2d47ad27b35855cea6f98ea64 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 23 Apr 2020 10:58:28 -0500 Subject: [PATCH] Drag and drop to rearrange filter criteria --- .../astrid/core/CriterionViewHolder.kt | 18 +++- .../astrid/core/CustomFilterAdapter.java | 35 ++++++-- .../core/CustomFilterItemTouchHelper.kt | 39 +++++++++ app/src/main/java/org/tasks/Callback2.java | 5 ++ .../activities/FilterSettingsActivity.java | 11 +++ app/src/main/res/layout/custom_filter_row.xml | 87 ++++++++++--------- 6 files changed, 149 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/todoroo/astrid/core/CustomFilterItemTouchHelper.kt create mode 100644 app/src/main/java/org/tasks/Callback2.java diff --git a/app/src/main/java/com/todoroo/astrid/core/CriterionViewHolder.kt b/app/src/main/java/com/todoroo/astrid/core/CriterionViewHolder.kt index 3b8351453..dca1a761a 100644 --- a/app/src/main/java/com/todoroo/astrid/core/CriterionViewHolder.kt +++ b/app/src/main/java/com/todoroo/astrid/core/CriterionViewHolder.kt @@ -1,5 +1,6 @@ package com.todoroo.astrid.core +import android.content.Context import android.view.View import android.widget.ImageView import android.widget.TextView @@ -10,8 +11,14 @@ import butterknife.OnClick import org.tasks.Callback import org.tasks.R import org.tasks.locale.Locale +import org.tasks.preferences.ResourceResolver -class CriterionViewHolder(itemView: View, private val locale: Locale, private val onClick: Callback) : RecyclerView.ViewHolder(itemView) { +class CriterionViewHolder( + private val context: Context, + itemView: View, + private val locale: Locale, + private val onClick: Callback) + : RecyclerView.ViewHolder(itemView) { @BindView(R.id.divider) lateinit var divider: View @@ -67,4 +74,13 @@ class CriterionViewHolder(itemView: View, private val locale: Locale, private va @OnClick(R.id.row) fun onClick() = this.onClick.call(criterion.id) + + fun setMoving(moving: Boolean) { + if (moving) { + row.setBackgroundColor(ResourceResolver.getData(context, R.attr.colorControlHighlight)) + } else { + row.setBackgroundResource(ResourceResolver.getResourceId(context, R.attr.selectableItemBackground)) + row.background.jumpToCurrentState() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/core/CustomFilterAdapter.java b/app/src/main/java/com/todoroo/astrid/core/CustomFilterAdapter.java index 776316b38..f1d5c88ff 100644 --- a/app/src/main/java/com/todoroo/astrid/core/CustomFilterAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/core/CustomFilterAdapter.java @@ -2,11 +2,15 @@ package com.todoroo.astrid.core; import static com.google.common.collect.Lists.transform; +import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.AsyncDifferConfig; import androidx.recyclerview.widget.AsyncListDiffer; +import androidx.recyclerview.widget.ListUpdateCallback; import androidx.recyclerview.widget.RecyclerView; import com.google.common.collect.ImmutableList; import java.util.List; @@ -14,7 +18,8 @@ import org.tasks.Callback; import org.tasks.R; import org.tasks.locale.Locale; -public class CustomFilterAdapter extends RecyclerView.Adapter { +public class CustomFilterAdapter extends RecyclerView.Adapter implements + ListUpdateCallback { private final Callback onClick; private final Locale locale; @@ -24,16 +29,16 @@ public class CustomFilterAdapter extends RecyclerView.Adapter objects, Locale locale, Callback onClick) { this.locale = locale; this.onClick = onClick; - differ = new AsyncListDiffer<>(this, new CriterionDiffCallback()); + differ = new AsyncListDiffer<>(this, new AsyncDifferConfig.Builder<>(new CriterionDiffCallback()).build()); submitList(objects); } @NonNull @Override public CriterionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = - LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_filter_row, parent, false); - return new CriterionViewHolder(view, locale, onClick); + Context context = parent.getContext(); + View view = LayoutInflater.from(context).inflate(R.layout.custom_filter_row, parent, false); + return new CriterionViewHolder(context, view, locale, onClick); } @Override @@ -53,4 +58,24 @@ public class CustomFilterAdapter extends RecyclerView.Adapter getItems() { return differ.getCurrentList(); } + + @Override + public void onInserted(int position, int count) { + notifyItemRangeInserted(position, count); + } + + @Override + public void onRemoved(int position, int count) { + notifyItemRangeRemoved(position, count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + notifyDataSetChanged(); + } + + @Override + public void onChanged(int position, int count, @Nullable Object payload) { + notifyItemRangeChanged(position, count); + } } diff --git a/app/src/main/java/com/todoroo/astrid/core/CustomFilterItemTouchHelper.kt b/app/src/main/java/com/todoroo/astrid/core/CustomFilterItemTouchHelper.kt new file mode 100644 index 000000000..57c9aaaf7 --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/core/CustomFilterItemTouchHelper.kt @@ -0,0 +1,39 @@ +package com.todoroo.astrid.core + +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import org.tasks.Callback2 + +class CustomFilterItemTouchHelper(private val onMove: Callback2, private val onClear: Runnable) : ItemTouchHelper.Callback() { + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { + return if (viewHolder.adapterPosition > 0) makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) else 0 + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + super.onSelectedChanged(viewHolder, actionState) + + if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { + (viewHolder as CriterionViewHolder).setMoving(true); + } + } + + override fun onMove( + recyclerView: RecyclerView, src: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + val toPosition = target.adapterPosition + if (toPosition == 0) { + return false + } + onMove.call(src.adapterPosition, toPosition); + return true + } + + override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { + super.clearView(recyclerView, viewHolder) + + (viewHolder as CriterionViewHolder).setMoving(false) + + onClear.run() + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/Callback2.java b/app/src/main/java/org/tasks/Callback2.java new file mode 100644 index 000000000..5d131ce86 --- /dev/null +++ b/app/src/main/java/org/tasks/Callback2.java @@ -0,0 +1,5 @@ +package org.tasks; + +public interface Callback2 { + void call(T1 t1, T2 t2); +} diff --git a/app/src/main/java/org/tasks/activities/FilterSettingsActivity.java b/app/src/main/java/org/tasks/activities/FilterSettingsActivity.java index 7314779d4..e453bb400 100644 --- a/app/src/main/java/org/tasks/activities/FilterSettingsActivity.java +++ b/app/src/main/java/org/tasks/activities/FilterSettingsActivity.java @@ -21,6 +21,7 @@ import android.widget.EditText; import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import butterknife.BindView; @@ -44,6 +45,7 @@ import com.todoroo.astrid.api.PermaSql; import com.todoroo.astrid.api.TextInputCriterion; import com.todoroo.astrid.core.CriterionInstance; import com.todoroo.astrid.core.CustomFilterAdapter; +import com.todoroo.astrid.core.CustomFilterItemTouchHelper; import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Task; @@ -116,8 +118,11 @@ public class FilterSettingsActivity extends BaseListSettingsActivity { criteria.add(instance); } adapter = new CustomFilterAdapter(criteria, locale, this::onClick); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(adapter); + new ItemTouchHelper(new CustomFilterItemTouchHelper(this::onMove, this::updateList)) + .attachToRecyclerView(recyclerView); fab.setExtended(isNew() || adapter.getItemCount() <= 1); @@ -126,6 +131,12 @@ public class FilterSettingsActivity extends BaseListSettingsActivity { updateTheme(); } + private void onMove(int from, int to) { + CriterionInstance criterion = criteria.remove(from); + criteria.add(to, criterion); + adapter.notifyItemMoved(from, to); + } + private void onClick(String replaceId) { CriterionInstance criterionInstance = find(criteria, c -> c.getId().equals(replaceId)); diff --git a/app/src/main/res/layout/custom_filter_row.xml b/app/src/main/res/layout/custom_filter_row.xml index c1e98f062..756482391 100644 --- a/app/src/main/res/layout/custom_filter_row.xml +++ b/app/src/main/res/layout/custom_filter_row.xml @@ -1,46 +1,53 @@ - + - + - + - + - + + + + + - +