diff options
Diffstat (limited to 'app/src/main/java/it/niedermann/nextcloud/deck/ui/filter')
7 files changed, 155 insertions, 50 deletions
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 aa6f59d04..6aa03b811 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 @@ -8,6 +8,7 @@ import android.os.Bundle; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -19,6 +20,7 @@ import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayoutMediator; +import it.niedermann.android.util.ColorUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogFilterBinding; import it.niedermann.nextcloud.deck.model.enums.EDueType; @@ -45,8 +47,9 @@ public class FilterDialogFragment extends BrandedDialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - indicator = getResources().getDrawable(R.drawable.circle_grey600_8dp); - indicator.setColorFilter(getResources().getColor(R.color.primary), PorterDuff.Mode.SRC_ATOP); + indicator = ContextCompat.getDrawable(requireContext(), R.drawable.circle_grey600_8dp); + assert indicator != null; + indicator.setColorFilter(getResources().getColor(R.color.defaultBrand), PorterDuff.Mode.SRC_ATOP); filterViewModel = new ViewModelProvider(requireActivity()).get(FilterViewModel.class); @@ -61,10 +64,10 @@ public class FilterDialogFragment extends BrandedDialogFragment { filterInformationDraft.observe(this, (draft) -> { switch (position) { case 0: - tab.setIcon(draft.getLabels().size() > 0 ? indicator : null); + tab.setIcon(draft.getLabels().size() > 0 || draft.isNoAssignedLabel() ? indicator : null); break; case 1: - tab.setIcon(draft.getUsers().size() > 0 ? indicator : null); + tab.setIcon(draft.getUsers().size() > 0 || draft.isNoAssignedUser() ? indicator : null); break; case 2: tab.setIcon(draft.getDueType() != EDueType.NO_FILTER ? indicator : null); @@ -103,9 +106,10 @@ public class FilterDialogFragment extends BrandedDialogFragment { @Override public void applyBrand(int mainColor) { - @ColorInt int finalMainColor = getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); - binding.tabLayout.setSelectedTabIndicatorColor(finalMainColor); - indicator.setColorFilter(finalMainColor, PorterDuff.Mode.SRC_ATOP); + @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(binding.tabLayout.getContext(), mainColor); + final boolean contrastRatioIsSufficient = ColorUtil.INSTANCE.getContrastRatio(mainColor, ContextCompat.getColor(binding.tabLayout.getContext(), R.color.primary)) > 1.7d; + binding.tabLayout.setSelectedTabIndicatorColor(contrastRatioIsSufficient ? mainColor : finalMainColor); + indicator.setColorFilter(contrastRatioIsSufficient ? mainColor : finalMainColor, PorterDuff.Mode.SRC_ATOP); } private static class TabsPagerAdapter extends FragmentStateAdapter { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsAdapter.java index 096f0db9c..39fb791be 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsAdapter.java @@ -1,20 +1,21 @@ package it.niedermann.nextcloud.deck.ui.filter; import android.content.res.ColorStateList; -import android.graphics.Color; import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; +import it.niedermann.android.util.ColorUtil; +import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemFilterLabelBinding; import it.niedermann.nextcloud.deck.model.Label; -import it.niedermann.nextcloud.deck.util.ColorUtil; @SuppressWarnings("WeakerAccess") public class FilterLabelsAdapter extends RecyclerView.Adapter<FilterLabelsAdapter.LabelViewHolder> { @@ -23,11 +24,17 @@ public class FilterLabelsAdapter extends RecyclerView.Adapter<FilterLabelsAdapte @NonNull private final List<Label> selectedLabels = new ArrayList<>(); @Nullable + private static final Label NOT_ASSIGNED = null; + @Nullable private final SelectionListener<Label> selectionListener; - public FilterLabelsAdapter(@NonNull List<Label> labels, @NonNull List<Label> selectedLabels, @Nullable SelectionListener<Label> selectionListener) { + public FilterLabelsAdapter(@NonNull List<Label> labels, @NonNull List<Label> selectedLabels, boolean noAssignedLabel, @Nullable SelectionListener<Label> selectionListener) { super(); + this.labels.add(NOT_ASSIGNED); this.labels.addAll(labels); + if (noAssignedLabel) { + this.selectedLabels.add(NOT_ASSIGNED); + } this.selectedLabels.addAll(selectedLabels); this.selectionListener = selectionListener; setHasStableIds(true); @@ -36,7 +43,8 @@ public class FilterLabelsAdapter extends RecyclerView.Adapter<FilterLabelsAdapte @Override public long getItemId(int position) { - return labels.get(position).getLocalId(); + @Nullable final Label label = labels.get(position); + return label == null ? -1L : label.getLocalId(); } @NonNull @@ -47,7 +55,11 @@ public class FilterLabelsAdapter extends RecyclerView.Adapter<FilterLabelsAdapte @Override public void onBindViewHolder(@NonNull LabelViewHolder viewHolder, int position) { - viewHolder.bind(labels.get(position)); + if (position == 0) { + viewHolder.bindNotAssigned(); + } else { + viewHolder.bind(labels.get(position)); + } } @Override @@ -55,26 +67,36 @@ public class FilterLabelsAdapter extends RecyclerView.Adapter<FilterLabelsAdapte return labels.size(); } - public List<Label> getSelected() { - return selectedLabels; - } - class LabelViewHolder extends RecyclerView.ViewHolder { private ItemFilterLabelBinding binding; LabelViewHolder(@NonNull ItemFilterLabelBinding binding) { super(binding.getRoot()); this.binding = binding; + this.binding.label.setClickable(false); } void bind(final Label label) { binding.label.setText(label.getTitle()); - final int labelColor = Color.parseColor("#" + label.getColor()); + final int labelColor = label.getColor(); binding.label.setChipBackgroundColor(ColorStateList.valueOf(labelColor)); - final int color = ColorUtil.getForegroundColorForBackgroundColor(labelColor); + final int color = ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(labelColor); binding.label.setTextColor(color); itemView.setSelected(selectedLabels.contains(label)); + bindClickListener(label); + } + + public void bindNotAssigned() { + binding.label.setText(itemView.getContext().getString(R.string.no_assigned_label)); + binding.label.setTextColor(ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.accent))); + binding.label.setChipIcon(ContextCompat.getDrawable(itemView.getContext(), R.drawable.ic_baseline_block_24)); + binding.label.setChipBackgroundColor(ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.primary))); + binding.label.setRippleColor(null); + itemView.setSelected(selectedLabels.contains(NOT_ASSIGNED)); + bindClickListener(NOT_ASSIGNED); + } + private void bindClickListener(@Nullable Label label) { itemView.setOnClickListener(view -> { if (selectedLabels.contains(label)) { selectedLabels.remove(label); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsFragment.java index 357f93cf9..e7d693185 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterLabelsFragment.java @@ -12,7 +12,6 @@ import androidx.lifecycle.ViewModelProvider; import it.niedermann.nextcloud.deck.databinding.DialogFilterLabelsBinding; import it.niedermann.nextcloud.deck.model.Label; -import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.MainViewModel; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; @@ -31,21 +30,33 @@ public class FilterLabelsFragment extends Fragment implements SelectionListener< filterViewModel = new ViewModelProvider(requireActivity()).get(FilterViewModel.class); - observeOnce(new SyncManager(requireContext()).findProposalsForLabelsToAssign(mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()), requireActivity(), (labels) -> { + observeOnce(filterViewModel.findProposalsForLabelsToAssign(mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()), requireActivity(), (labels) -> { binding.labels.setNestedScrollingEnabled(false); - binding.labels.setAdapter(new FilterLabelsAdapter(labels, requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).getLabels(), this)); + binding.labels.setAdapter(new FilterLabelsAdapter( + labels, + requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).getLabels(), + requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).isNoAssignedLabel(), + this)); }); return binding.getRoot(); } @Override - public void onItemSelected(Label item) { - filterViewModel.addFilterInformationDraftLabel(item); + public void onItemSelected(@Nullable Label item) { + if (item == null) { + filterViewModel.setNotAssignedLabel(true); + } else { + filterViewModel.addFilterInformationDraftLabel(item); + } } @Override - public void onItemDeselected(Label item) { - filterViewModel.removeFilterInformationLabel(item); + public void onItemDeselected(@Nullable Label item) { + if (item == null) { + filterViewModel.setNotAssignedLabel(false); + } else { + filterViewModel.removeFilterInformationLabel(item); + } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java index b4ae8f679..4b75b985f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java @@ -8,6 +8,8 @@ import androidx.annotation.Nullable; import androidx.annotation.Px; import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.Glide; + import java.util.ArrayList; import java.util.List; @@ -23,6 +25,8 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us final int avatarSize; @NonNull private final Account account; + @Nullable + private static final User NOT_ASSIGNED = null; @NonNull private final List<User> users = new ArrayList<>(); @NonNull @@ -30,11 +34,15 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us @Nullable private final SelectionListener<User> selectionListener; - public FilterUserAdapter(@Px int avatarSize, @NonNull Account account, @NonNull List<User> users, @NonNull List<User> selectedUsers, @Nullable SelectionListener selectionListener) { + public FilterUserAdapter(@Px int avatarSize, @NonNull Account account, @NonNull List<User> users, @NonNull List<User> selectedUsers, boolean noAssignedUser, @Nullable SelectionListener<User> selectionListener) { super(); this.avatarSize = avatarSize; this.account = account; + this.users.add(NOT_ASSIGNED); this.users.addAll(users); + if (noAssignedUser) { + this.selectedUsers.add(NOT_ASSIGNED); + } this.selectedUsers.addAll(selectedUsers); this.selectionListener = selectionListener; setHasStableIds(true); @@ -43,7 +51,8 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us @Override public long getItemId(int position) { - return users.get(position).getLocalId(); + @Nullable final User user = users.get(position); + return user == null ? -1L : user.getLocalId(); } @NonNull @@ -54,7 +63,11 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us @Override public void onBindViewHolder(@NonNull UserViewHolder viewHolder, int position) { - viewHolder.bind(users.get(position)); + if (position == 0) { + viewHolder.bindNotAssigned(); + } else { + viewHolder.bind(users.get(position)); + } } @Override @@ -62,10 +75,6 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us return users.size(); } - public List<User> getSelected() { - return selectedUsers; - } - class UserViewHolder extends RecyclerView.ViewHolder { private ItemFilterUserBinding binding; @@ -74,22 +83,34 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us this.binding = binding; } - void bind(final User user) { - binding.displayName.setText(user.getDisplayname()); + void bind(@NonNull final User user) { + binding.title.setText(user.getDisplayname()); ViewUtil.addAvatar(binding.avatar, account.getUrl(), user.getUid(), avatarSize, R.drawable.ic_person_grey600_24dp); itemView.setSelected(selectedUsers.contains(user)); + bindClickListener(user); + } + + public void bindNotAssigned() { + binding.title.setText(itemView.getContext().getString(R.string.simple_unassigned)); + Glide.with(itemView.getContext()) + .load(R.drawable.ic_baseline_block_24) + .into(binding.avatar); + itemView.setSelected(selectedUsers.contains(NOT_ASSIGNED)); + bindClickListener(NOT_ASSIGNED); + } + private void bindClickListener(@Nullable User user) { itemView.setOnClickListener(view -> { if (selectedUsers.contains(user)) { selectedUsers.remove(user); itemView.setSelected(false); - if(selectionListener != null) { + if (selectionListener != null) { selectionListener.onItemDeselected(user); } } else { selectedUsers.add(user); itemView.setSelected(true); - if(selectionListener != null) { + if (selectionListener != null) { selectionListener.onItemSelected(user); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserFragment.java index 64bc1db1f..6ffaec6a6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserFragment.java @@ -10,14 +10,13 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; +import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogFilterAssigneesBinding; import it.niedermann.nextcloud.deck.model.User; -import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.MainViewModel; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.util.DimensionUtil.dpToPx; import static java.util.Objects.requireNonNull; public class FilterUserFragment extends Fragment implements SelectionListener<User> { @@ -33,21 +32,35 @@ public class FilterUserFragment extends Fragment implements SelectionListener<Us filterViewModel = new ViewModelProvider(requireActivity()).get(FilterViewModel.class); - observeOnce(new SyncManager(requireContext()).findProposalsForUsersToAssign(mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()), requireActivity(), (users) -> { + observeOnce(filterViewModel.findProposalsForUsersToAssign(mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()), requireActivity(), (users) -> { binding.users.setNestedScrollingEnabled(false); - binding.users.setAdapter(new FilterUserAdapter(dpToPx(requireContext(), R.dimen.avatar_size), mainViewModel.getCurrentAccount(), users, requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).getUsers(), this)); + binding.users.setAdapter(new FilterUserAdapter( + DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.avatar_size), + mainViewModel.getCurrentAccount(), + users, + requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).getUsers(), + requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).isNoAssignedUser(), + this)); }); return binding.getRoot(); } @Override - public void onItemSelected(User item) { - filterViewModel.addFilterInformationUser(item); + public void onItemSelected(@Nullable User item) { + if (item == null) { + filterViewModel.setNotAssignedUser(true); + } else { + filterViewModel.addFilterInformationUser(item); + } } @Override - public void onItemDeselected(User item) { - filterViewModel.removeFilterInformationUser(item); + public void onItemDeselected(@Nullable User item) { + if (item == null) { + filterViewModel.setNotAssignedUser(false); + } else { + filterViewModel.removeFilterInformationUser(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 cf8dc1754..c42a61ebd 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 @@ -1,28 +1,40 @@ package it.niedermann.nextcloud.deck.ui.filter; +import android.app.Application; + import androidx.annotation.IntRange; import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; + +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.EDueType; import it.niedermann.nextcloud.deck.model.internal.FilterInformation; +import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import static it.niedermann.nextcloud.deck.model.internal.FilterInformation.hasActiveFilter; @SuppressWarnings("WeakerAccess") -public class FilterViewModel extends ViewModel { +public class FilterViewModel extends AndroidViewModel { + + private final SyncManager syncManager; @IntRange(from = 0, to = 2) private int currentFilterTab = 0; @NonNull - private MutableLiveData<FilterInformation> filterInformationDraft = new MutableLiveData<>(new FilterInformation()); + private final MutableLiveData<FilterInformation> filterInformationDraft = new MutableLiveData<>(new FilterInformation()); @NonNull - private MutableLiveData<FilterInformation> filterInformation = new MutableLiveData<>(); + private final MutableLiveData<FilterInformation> filterInformation = new MutableLiveData<>(); + + public FilterViewModel(@NonNull Application application) { + super(application); + this.syncManager = new SyncManager(application); + } public void publishFilterInformationDraft() { this.filterInformation.postValue(hasActiveFilter(filterInformationDraft.getValue()) ? filterInformationDraft.getValue() : null); @@ -66,6 +78,18 @@ public class FilterViewModel extends ViewModel { this.filterInformationDraft.postValue(newDraft); } + public void setNotAssignedUser(boolean notAssignedUser) { + FilterInformation newDraft = new FilterInformation(filterInformationDraft.getValue()); + newDraft.setNoAssignedUser(notAssignedUser); + this.filterInformationDraft.postValue(newDraft); + } + + public void setNotAssignedLabel(boolean notAssignedLabel) { + FilterInformation newDraft = new FilterInformation(filterInformationDraft.getValue()); + newDraft.setNoAssignedLabel(notAssignedLabel); + this.filterInformationDraft.postValue(newDraft); + } + public void removeFilterInformationLabel(@NonNull Label label) { FilterInformation newDraft = new FilterInformation(filterInformationDraft.getValue()); newDraft.removeLabel(label); @@ -86,4 +110,12 @@ public class FilterViewModel extends ViewModel { public int getCurrentFilterTab() { return this.currentFilterTab; } + + public LiveData<List<User>> findProposalsForUsersToAssign(final long accountId, long boardId) { + return syncManager.findProposalsForUsersToAssign(accountId, boardId, -1L, -1); + } + + public LiveData<List<Label>> findProposalsForLabelsToAssign(final long accountId, final long boardId) { + return syncManager.findProposalsForLabelsToAssign(accountId, boardId, -1L); + } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/SelectionListener.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/SelectionListener.java index d2635a860..3fad71773 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/SelectionListener.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/SelectionListener.java @@ -1,9 +1,11 @@ package it.niedermann.nextcloud.deck.ui.filter; +import androidx.annotation.Nullable; + public interface SelectionListener<T> { - void onItemSelected(T item); + void onItemSelected(@Nullable T item); - default void onItemDeselected(T item) { + default void onItemDeselected(@Nullable T item) { // Deselecting is optional } } |