diff options
author | Stefan Niedermann <info@niedermann.it> | 2024-01-18 15:30:06 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2024-01-18 15:30:06 +0300 |
commit | eea21c772af0b525674837ff3aa3d1e6ec9d380b (patch) | |
tree | 641142c8920e5dc8ba3ef06ecd8518be47d53e2f /app/src | |
parent | 55abed7577387ba3b2167238882e81b80ce4cba5 (diff) |
feat(done): Allow filtering for (un)done cards
Refs: #1556 https://github.com/nextcloud/deck/issues/5406
Signed-off-by: Stefan Niedermann <info@niedermann.it>
Diffstat (limited to 'app/src')
12 files changed, 279 insertions, 8 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java index 5d3b28950..a7cd3a2af 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java @@ -60,6 +60,7 @@ import it.niedermann.nextcloud.deck.model.Stack; import it.niedermann.nextcloud.deck.model.User; import it.niedermann.nextcloud.deck.model.appwidgets.StackWidgetModel; import it.niedermann.nextcloud.deck.model.enums.DBStatus; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.model.full.FullCard; @@ -259,9 +260,9 @@ public class DataBaseAdapter { public LiveData<List<FullCard>> getFullCardsForStack(long accountId, long localStackId, @Nullable FilterInformation filter) { return new ReactiveLiveData<>( - filter == null - ? db.getCardDao().getFullCardsForStack(accountId, localStackId) - : db.getCardDao().getFilteredFullCardsForStack(getQueryForFilter(filter, accountId, localStackId))) + FilterInformation.hasActiveFilter(filter) + ? db.getCardDao().getFilteredFullCardsForStack(getQueryForFilter(filter, accountId, localStackId)) + : db.getCardDao().getFullCardsForStack(accountId, localStackId)) .tap(this::filterRelationsForCard, executor) .distinctUntilChanged(); @@ -284,9 +285,9 @@ public class DataBaseAdapter { @WorkerThread public List<FullCard> getFullCardsForStackDirectly(long accountId, long localStackId, @Nullable FilterInformation filter) { - return filter == null - ? db.getCardDao().getFullCardsForStackDirectly(accountId, localStackId) - : db.getCardDao().getFilteredFullCardsForStackDirectly(getQueryForFilter(filter, accountId, localStackId)); + return FilterInformation.hasActiveFilter(filter) + ? db.getCardDao().getFilteredFullCardsForStackDirectly(getQueryForFilter(filter, accountId, localStackId)) + : db.getCardDao().getFullCardsForStackDirectly(accountId, localStackId); } @AnyThread @@ -369,6 +370,20 @@ public class DataBaseAdapter { throw new IllegalArgumentException("You need to add your new EDueType value\"" + filter.getDueType() + "\" here!"); } } + + if (filter.getDoneType() != EDoneType.NO_FILTER) { + switch (filter.getDoneType()) { + case DONE: + query.append("and (c.done is not null and c.done != 0)"); + break; + case UNDONE: + query.append("and (c.done is null or c.done = 0)"); + break; + default: + throw new IllegalArgumentException("You need to add your new EDueType value\"" + filter.getDueType() + "\" here!"); + } + } + if (!TextUtils.isEmpty(filter.getFilterText())) { query.append(" and (c.description like ? or c.title like ?) "); String filterText = "%" + filter.getFilterText() + "%"; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/enums/EDoneType.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/enums/EDoneType.java new file mode 100644 index 000000000..4222ee487 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/enums/EDoneType.java @@ -0,0 +1,40 @@ +package it.niedermann.nextcloud.deck.model.enums; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import it.niedermann.nextcloud.deck.R; + +public enum EDoneType { + NO_FILTER(1, R.string.filter_done_no_filter), + DONE(2, R.string.filter_done_done), + UNDONE(3, R.string.filter_done_undone); + + private final int value; + private final int id; + + EDoneType(int id, @StringRes int value) { + this.value = value; + this.id = id; + } + + public int getId() { + return id; + } + + public static EDoneType findById(int id) { + for (EDoneType s : EDoneType.values()) { + if (s.getId() == id) { + return s; + } + } + throw new IllegalArgumentException("unknown " + EDoneType.class.getSimpleName() + " key: " + id); + } + + @NonNull + public String toString(Context context) { + return context.getString(this.value); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java index da0501779..8c7da2b24 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java @@ -9,6 +9,7 @@ import java.util.List; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.User; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.ocs.projects.OcsProject; @@ -20,6 +21,8 @@ public class FilterInformation implements Serializable { @NonNull private EDueType dueType = EDueType.NO_FILTER; + @NonNull + private EDoneType doneType = EDoneType.NO_FILTER; private boolean noAssignedLabel = false; private boolean noAssignedUser = false; private boolean noAssignedProject = false; @@ -41,6 +44,7 @@ public class FilterInformation implements Serializable { public FilterInformation(@Nullable FilterInformation filterInformation) { if (filterInformation != null) { this.dueType = filterInformation.getDueType(); + this.doneType = filterInformation.getDoneType(); this.archiveStatus = filterInformation.getArchiveStatus(); this.users.addAll(filterInformation.getUsers()); this.labels.addAll(filterInformation.getLabels()); @@ -72,6 +76,15 @@ public class FilterInformation implements Serializable { } @NonNull + public EDoneType getDoneType() { + return doneType; + } + + public void setDoneType(@NonNull EDoneType doneType) { + this.doneType = doneType; + } + + @NonNull public List<User> getUsers() { return users; } @@ -152,6 +165,7 @@ public class FilterInformation implements Serializable { public String toString() { return "FilterInformation{" + "dueType=" + dueType + + ", doneType=" + doneType + ", noAssignedLabel=" + noAssignedLabel + ", noAssignedUser=" + noAssignedUser + ", users=" + users + @@ -169,6 +183,7 @@ public class FilterInformation implements Serializable { return false; } return !(filterInformation.getDueType() == EDueType.NO_FILTER + && filterInformation.getDoneType() == EDoneType.NO_FILTER && filterInformation.getUsers().isEmpty() && filterInformation.getProjects().isEmpty() && filterInformation.getLabels().isEmpty() diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java index 7a6aba31d..807130a0d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java @@ -182,7 +182,7 @@ public class Version implements Comparable<Version> { * @see <a href="https://github.com/nextcloud/deck/issues/534">Deck server issue #534</a> */ public boolean supportsDone() { - return isGreaterOrEqualTo(VERSION_1_12_2); + return isGreaterOrEqualTo(VERSION_1_12_0); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java index 8eac0dbb4..65c7f824e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java @@ -19,6 +19,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogFilterBinding; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; @@ -32,6 +33,7 @@ public class FilterDialogFragment extends ThemedDialogFragment { private final static int[] tabTitles = new int[]{ R.string.filter_tags_title, R.string.filter_user_title, + R.string.filter_done_title, R.string.filter_duedate_title }; @@ -63,6 +65,9 @@ public class FilterDialogFragment extends ThemedDialogFragment { tab.setIcon(draft.getUsers().size() > 0 || draft.isNoAssignedUser() ? indicator : null); break; case 2: + tab.setIcon(draft.getDoneType() != EDoneType.NO_FILTER ? indicator : null); + break; + case 3: tab.setIcon(draft.getDueType() != EDueType.NO_FILTER ? indicator : null); break; default: @@ -124,6 +129,8 @@ public class FilterDialogFragment extends ThemedDialogFragment { case 1: return new FilterUserFragment(); case 2: + return new FilterDoneTypeFragment(); + case 3: return new FilterDueTypeFragment(); default: throw new IllegalArgumentException("position must be between 0 and 2 but was " + position); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeAdapter.java new file mode 100644 index 000000000..d194f78ab --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeAdapter.java @@ -0,0 +1,96 @@ +package it.niedermann.nextcloud.deck.ui.filter; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.Arrays; + +import it.niedermann.nextcloud.deck.databinding.ItemFilterDonetypeBinding; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.Themed; + +public class FilterDoneTypeAdapter extends RecyclerView.Adapter<FilterDoneTypeAdapter.DoneTypeViewHolder> { + @NonNull + private final EDoneType[] doneTypes = EDoneType.values(); + private int selectedDoneTypePosition; + @Nullable + private final SelectionListener<EDoneType> selectionListener; + @ColorInt + private final int color; + + @SuppressWarnings("WeakerAccess") + public FilterDoneTypeAdapter(@NonNull EDoneType selectedDoneType, @Nullable SelectionListener<EDoneType> selectionListener, @ColorInt int color) { + super(); + this.selectedDoneTypePosition = Arrays.binarySearch(doneTypes, selectedDoneType); + this.selectionListener = selectionListener; + this.color = color; + setHasStableIds(true); + } + + @NonNull + @Override + public DoneTypeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new DoneTypeViewHolder(ItemFilterDonetypeBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull DoneTypeViewHolder viewHolder, int position) { + viewHolder.bind(doneTypes[position]); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return doneTypes.length; + } + + class DoneTypeViewHolder extends RecyclerView.ViewHolder implements Themed { + private final ItemFilterDonetypeBinding binding; + + DoneTypeViewHolder(@NonNull ItemFilterDonetypeBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + + void bind(final EDoneType doneType) { + binding.doneType.setText(doneType.toString(binding.doneType.getContext())); + itemView.setSelected(doneTypes[selectedDoneTypePosition].equals(doneType)); + applyTheme(color); + + itemView.setOnClickListener(view -> { + final int oldSelection = selectedDoneTypePosition; + if (doneTypes[selectedDoneTypePosition].equals(doneType)) { + selectedDoneTypePosition = Arrays.binarySearch(doneTypes, EDoneType.NO_FILTER); + itemView.setSelected(false); + if (selectionListener != null) { + selectionListener.onItemSelected(EDoneType.NO_FILTER); + } + notifyItemChanged(selectedDoneTypePosition); + } else { + selectedDoneTypePosition = Arrays.binarySearch(doneTypes, doneType); + itemView.setSelected(true); + if (selectionListener != null) { + selectionListener.onItemSelected(doneType); + } + } + notifyItemChanged(oldSelection); + }); + } + + @Override + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, itemView.getContext()); + utils.deck.themeSelectedCheck(binding.selectedCheck.getContext(), binding.selectedCheck.getDrawable()); + } + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeFragment.java new file mode 100644 index 000000000..8cee39d1e --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeFragment.java @@ -0,0 +1,47 @@ +package it.niedermann.nextcloud.deck.ui.filter; + +import static java.util.Objects.requireNonNull; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import it.niedermann.nextcloud.deck.databinding.DialogFilterDoneBinding; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; + +public class FilterDoneTypeFragment extends Fragment implements SelectionListener<EDoneType> { + + private FilterViewModel filterViewModel; + private DialogFilterDoneBinding binding; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + binding = DialogFilterDoneBinding.inflate(requireActivity().getLayoutInflater()); + + filterViewModel = new ViewModelProvider(requireActivity()).get(FilterViewModel.class); + + binding.doneType.setItemAnimator(null); + filterViewModel.getCurrentBoardColor$().observe(getViewLifecycleOwner(), + color -> binding.doneType.setAdapter(new FilterDoneTypeAdapter(requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).getDoneType(), this, color))); + + return binding.getRoot(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.binding = null; + } + + @Override + public void onItemSelected(EDoneType item) { + filterViewModel.setFilterInformationDraftDoneType(item); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java index 09f2f4fa1..5bd226e9f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java @@ -15,6 +15,7 @@ import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.User; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.internal.FilterInformation; import it.niedermann.nextcloud.deck.ui.viewmodel.BaseViewModel; @@ -80,6 +81,12 @@ public class FilterViewModel extends BaseViewModel { this.filterInformationDraft.postValue(newDraft); } + public void setFilterInformationDraftDoneType(@NonNull EDoneType doneType) { + final var newDraft = new FilterInformation(filterInformationDraft.getValue()); + newDraft.setDoneType(doneType); + this.filterInformationDraft.postValue(newDraft); + } + public void addFilterInformationDraftLabel(@NonNull Label label) { final var newDraft = new FilterInformation(filterInformationDraft.getValue()); newDraft.addLabel(label); diff --git a/app/src/main/res/layout/dialog_filter.xml b/app/src/main/res/layout/dialog_filter.xml index b95c2b0c8..2b1e3499c 100644 --- a/app/src/main/res/layout/dialog_filter.xml +++ b/app/src/main/res/layout/dialog_filter.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" android:background="@null" app:tabInlineLabel="true" - app:tabMode="fixed" + app:tabMode="auto" app:tabUnboundedRipple="true" /> <androidx.viewpager2.widget.ViewPager2 diff --git a/app/src/main/res/layout/dialog_filter_done.xml b/app/src/main/res/layout/dialog_filter_done.xml new file mode 100644 index 000000000..5cf3fedb0 --- /dev/null +++ b/app/src/main/res/layout/dialog_filter_done.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/doneType" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="?attr/dialogPreferredPadding" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/item_filter_duetype" />
\ No newline at end of file diff --git a/app/src/main/res/layout/item_filter_donetype.xml b/app/src/main/res/layout/item_filter_donetype.xml new file mode 100644 index 000000000..9cf823c77 --- /dev/null +++ b/app/src/main/res/layout/item_filter_donetype.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal"> + + <TextView + android:id="@+id/doneType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="middle" + android:padding="@dimen/spacer_2x" + android:textAppearance="?attr/textAppearanceListItem" + tools:text="@tools:sample/lorem" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/selected_check" + android:layout_width="22dp" + android:layout_height="22dp" + android:layout_marginStart="@dimen/spacer_1x" + app:layout_alignSelf="center" + app:layout_flexShrink="0" + app:srcCompat="@drawable/selected_check" + tools:src="@drawable/ic_check_grey600_24dp" /> + +</com.google.android.flexbox.FlexboxLayout>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ec1b84a5d..dabf27783 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -197,6 +197,10 @@ <string name="filter_no_due">No due date</string> <string name="filter_later">Later</string> + <string name="filter_done_no_filter">All</string> + <string name="filter_done_done">Completed</string> + <string name="filter_done_undone">Uncompleted</string> + <string name="archived_cards">Archived cards</string> <string name="attachment_already_exists">Attachment already exists</string> <string name="pick_custom_color">Pick custom color</string> @@ -208,6 +212,7 @@ <string name="filter_tags_title">Tags</string> <string name="filter_user_title">Users</string> <string name="filter_duedate_title">Due date</string> + <string name="filter_done_title">Completed</string> <string name="action_board_dearchive">Undo board archiving</string> <string name="archived_boards">Archived boards</string> |