Add bottomsheet for chips

This commit is contained in:
Alexander Bakker 2020-12-22 13:29:38 +01:00 committed by Michael Schättgen
parent c6cb390990
commit a6bf3b7c87
16 changed files with 255 additions and 66 deletions

View file

@ -41,6 +41,8 @@ import com.beemdevelopment.aegis.vault.VaultManager;
import com.beemdevelopment.aegis.vault.VaultManagerException;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
@ -58,6 +60,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
public class MainActivity extends AegisActivity implements EntryListView.Listener {
// activity request codes
@ -76,7 +79,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
private AegisApplication _app;
private VaultManager _vault;
private boolean _loaded;
private String _selectedGroup;
private List<String> _selectedGroups;
private boolean _searchSubmitted;
private boolean _isAuthenticating;
@ -247,7 +250,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
} else {
intent.putExtra("entryUUID", entry.getUUID());
}
intent.putExtra("selectedGroup", _selectedGroup);
intent.putExtra("selectedGroup", (ArrayList<String>) _selectedGroups);
startActivityForResult(intent, requestCode);
}
@ -319,45 +322,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
}
}
private void updateGroupFilterMenu() {
SubMenu menu = _menu.findItem(R.id.action_filter).getSubMenu();
for (int i = menu.size() - 1; i >= 0; i--) {
MenuItem item = menu.getItem(i);
if (item.getItemId() == R.id.menu_filter_all) {
continue;
}
menu.removeItem(item.getItemId());
}
// if the group no longer exists, switch back to 'All'
TreeSet<String> groups = _vault.getGroups();
if (_selectedGroup != null && !groups.contains(_selectedGroup)) {
menu.findItem(R.id.menu_filter_all).setChecked(true);
setGroupFilter(null);
}
for (String group : groups) {
MenuItem item = menu.add(R.id.action_filter_group, Menu.NONE, Menu.NONE, group);
if (group.equals(_selectedGroup)) {
item.setChecked(true);
}
}
if (groups.size() > 0) {
menu.add(R.id.action_filter_group, Menu.NONE, 10, R.string.filter_ungrouped);
}
menu.setGroupCheckable(R.id.action_filter_group, true, true);
}
private void updateSortCategoryMenu() {
SortCategory category = getPreferences().getCurrentSortCategory();
_menu.findItem(category.getMenuItem()).setChecked(true);
}
private void setGroupFilter(String group) {
getSupportActionBar().setSubtitle(group);
_selectedGroup = group;
private void setGroupFilter(List<String> group) {
_selectedGroups = group;
_entryListView.setGroupFilter(group, true);
}
@ -492,7 +463,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
} else if (_loaded) {
// update the list of groups in the filter menu
if (_menu != null) {
updateGroupFilterMenu();
_entryListView.setGroups(_vault.getGroups());
}
// refresh all codes to prevent showing old ones
@ -516,7 +487,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
collapseSearchView();
setTitle("Aegis");
setGroupFilter(_selectedGroup);
setGroupFilter(_selectedGroups);
return;
}
@ -543,7 +514,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
getMenuInflater().inflate(R.menu.menu_main, menu);
updateLockIcon();
if (_loaded) {
updateGroupFilterMenu();
_entryListView.setGroups(_vault.getGroups());
updateSortCategoryMenu();
}
@ -596,7 +567,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
_app.lock(true);
return true;
default:
if (item.getGroupId() == R.id.action_filter_group) {
/*if (item.getGroupId() == R.id.action_filter_group) {
item.setChecked(true);
String group = null;
@ -604,7 +575,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
group = item.getTitle().toString();
}
setGroupFilter(group);
}
}*/
if (item.getGroupId() == R.id.action_sort_category) {
item.setChecked(true);
@ -811,7 +782,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
for (VaultEntry entry : _selectedEntries) {
if (entry.getGroup() != null) {
if (!_vault.getGroups().contains(entry.getGroup())) {
updateGroupFilterMenu();
_entryListView.setGroups(_vault.getGroups());
}
}
}

View file

@ -25,6 +25,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements ItemTouchHelperAdapter {
@ -40,7 +41,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
private boolean _tapToReveal;
private int _tapToRevealTime;
private boolean _copyOnTap;
private String _groupFilter;
private List<String> _groupFilter;
private SortCategory _sortCategory;
private ViewMode _viewMode;
private String _searchFilter;
@ -56,6 +57,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
_entries = new ArrayList<>();
_shownEntries = new ArrayList<>();
_selectedEntries = new ArrayList<>();
_groupFilter = new ArrayList<>();
_holders = new ArrayList<>();
_dimHandler = new Handler();
_view = view;
@ -207,12 +209,12 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
String issuer = entry.getIssuer().toLowerCase();
String name = entry.getName().toLowerCase();
if (_groupFilter != null) {
if (group == null && _groupFilter.equals(_view.getContext().getString(R.string.filter_ungrouped))) {
if (!_groupFilter.isEmpty()) {
if (group == null && _groupFilter.contains(_view.getContext().getString(R.string.filter_ungrouped))) {
return false;
}
if (group == null || !group.equals(_groupFilter)) {
if (group == null || !_groupFilter.contains(group)) {
return true;
}
}
@ -234,11 +236,16 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
}
}
public void setGroupFilter(String group, boolean apply) {
if (_groupFilter != null && _groupFilter.equals(group)) {
public void setGroupFilter(List<String> groups, boolean apply) {
if(groups == null) {
groups = new ArrayList<>();
}
if (_groupFilter.equals(groups)) {
return;
}
_groupFilter = group;
_groupFilter = groups;
if (apply) {
updateShownEntries();
checkPeriodUniformity();
@ -286,6 +293,10 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
_viewMode = viewMode;
}
public void setGroups(TreeSet<String> groups) {
_view.setGroups(groups);
}
@Override
public void onItemDismiss(int position) {
@ -294,7 +305,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
@Override
public void onItemDrop(int position) {
// moving entries is not allowed when a filter is applied
if (_groupFilter != null) {
if (!_groupFilter.isEmpty()) {
return;
}
@ -304,7 +315,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
@Override
public void onItemMove(int firstPosition, int secondPosition) {
// moving entries is not allowed when a filter is applied
if (_groupFilter != null) {
if (!_groupFilter.isEmpty()) {
return;
}
@ -591,7 +602,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
}
public boolean isDragAndDropAllowed() {
return _sortCategory == SortCategory.CUSTOM && _groupFilter == null && _searchFilter == null;
return _sortCategory == SortCategory.CUSTOM && _groupFilter.isEmpty() && _searchFilter == null;
}
public boolean isPeriodUniform() {

View file

@ -2,6 +2,7 @@ package com.beemdevelopment.aegis.ui.views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -10,6 +11,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.widget.Button;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@ -28,6 +30,7 @@ import com.beemdevelopment.aegis.helpers.MetricsHelper;
import com.beemdevelopment.aegis.helpers.SimpleItemTouchHelperCallback;
import com.beemdevelopment.aegis.helpers.UiRefresher;
import com.beemdevelopment.aegis.otp.TotpInfo;
import com.beemdevelopment.aegis.ui.Dialogs;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.bumptech.glide.Glide;
import com.bumptech.glide.ListPreloader;
@ -35,11 +38,17 @@ import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.util.ViewPreloadSizeProvider;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
public class EntryListView extends Fragment implements EntryAdapter.Listener {
private EntryAdapter _adapter;
@ -53,7 +62,10 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
private TotpProgressBar _progressBar;
private boolean _showProgress;
private ViewMode _viewMode;
private TreeSet<String> _groups;
private LinearLayout _emptyStateView;
private Chip _groupChip;
private List<String> _groupFilter;
private UiRefresher _refresher;
@ -74,6 +86,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_entry_list_view, container, false);
_progressBar = view.findViewById(R.id.progressBar);
_groupChip = view.findViewById(R.id.chip_group);
// set up the recycler view
_recyclerView = view.findViewById(R.id.rvKeyProfiles);
@ -129,7 +142,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
super.onDestroyView();
}
public void setGroupFilter(String group, boolean apply) {
public void setGroupFilter(List<String> group, boolean apply) {
_adapter.setGroupFilter(group, apply);
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());
@ -339,11 +352,67 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
_recyclerView.scheduleLayoutAnimation();
}
private void initializeGroupChip() {
if (_groups.isEmpty()) {
_groupChip.setVisibility(View.GONE);
return;
}
View view = getLayoutInflater().inflate(R.layout.dialog_select_groups, null);
BottomSheetDialog dialog = new BottomSheetDialog(getContext());
dialog.setContentView(view);
ColorStateList colorStateList = ContextCompat.getColorStateList(getContext(), R.color.bg_chip_text_color);
ChipGroup chipGroup = view.findViewById(R.id.groupChipGroup);
String groupChipText = _groupChip.getText().toString();
chipGroup.removeAllViews();
for (String group : _groups) {
Chip chip = new Chip(getContext());
chip.setText(group);
chip.setCheckable(true);
chip.setCheckedIconVisible(false);
chip.setChipBackgroundColorResource(R.color.bg_chip_color);
chip.setTextColor(colorStateList);
chip.setOnCheckedChangeListener((group1, checkedId) -> {
List<String> groupFilter = chipGroup.getCheckedChipIds().stream()
.map(i -> ((Chip) view.findViewById(i)).getText().toString())
.collect(Collectors.toList());
_groupFilter = groupFilter;
setGroupFilter(groupFilter, true);
if (_groupFilter.isEmpty()) {
_groupChip.setText(groupChipText);
} else {
_groupChip.setText(String.format("%s (%d)", getString(R.string.groups), _groupFilter.size()));
}
});
chipGroup.addView(chip);
}
Button clearButton = view.findViewById(R.id.btnClear);
clearButton.setOnClickListener(v -> {
chipGroup.clearCheck();
setGroupFilter(null, true);
dialog.dismiss();
});
_groupChip.setOnClickListener(v -> {
Dialogs.showSecureDialog(dialog);
});
}
private void setShowProgress(boolean showProgress) {
_showProgress = showProgress;
updateDividerDecoration();
}
public void setGroups(TreeSet<String> groups) {
_groups = groups;
initializeGroupChip();
}
private void updateDividerDecoration() {
if (_dividerDecoration != null) {
_recyclerView.removeItemDecoration(_dividerDecoration);
@ -394,10 +463,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) == 0) {
// the first item should also have a top margin
outRect.top = _height;
}
outRect.bottom = _height;
}
}