mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-22 23:09:13 +00:00
Merge pull request #1479 from michaelschattgen/feature/group-chipgroup
Improve group filters
This commit is contained in:
commit
8b8e071831
7 changed files with 215 additions and 189 deletions
|
@ -177,12 +177,10 @@ public class OverallTest extends AegisTest {
|
|||
}
|
||||
|
||||
private void changeGroupFilter(String text) {
|
||||
onView(withId(R.id.chip_group)).perform(click());
|
||||
if (text == null) {
|
||||
onView(withId(R.id.btnClear)).perform(click());
|
||||
onView(allOf(withText(R.string.all), isDescendantOfA(withId(R.id.groupChipGroup)))).perform(click());
|
||||
} else {
|
||||
onView(withText(text)).perform(click());
|
||||
onView(isRoot()).perform(pressBack());
|
||||
onView(allOf(withText(text), isDescendantOfA(withId(R.id.groupChipGroup)))).perform(click());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import androidx.activity.OnBackPressedCallback;
|
|||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
|
@ -50,21 +51,29 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
|||
import com.beemdevelopment.aegis.ui.fragments.preferences.BackupsPreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.fragments.preferences.PreferencesFragment;
|
||||
import com.beemdevelopment.aegis.ui.models.ErrorCardInfo;
|
||||
import com.beemdevelopment.aegis.ui.models.VaultGroupModel;
|
||||
import com.beemdevelopment.aegis.ui.tasks.QrDecodeTask;
|
||||
import com.beemdevelopment.aegis.ui.views.EntryListView;
|
||||
import com.beemdevelopment.aegis.util.TimeUtils;
|
||||
import com.beemdevelopment.aegis.util.UUIDMap;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultFile;
|
||||
import com.beemdevelopment.aegis.vault.VaultGroup;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepository;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepositoryException;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -91,6 +100,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
private SearchView _searchView;
|
||||
private EntryListView _entryListView;
|
||||
|
||||
private Collection<VaultGroup> _groups;
|
||||
private ChipGroup _groupChip;
|
||||
private Set<UUID> _groupFilter;
|
||||
private Set<UUID> _prefGroupFilter;
|
||||
|
||||
private FabScrollHelper _fabScrollHelper;
|
||||
|
||||
private ActionMode _actionMode;
|
||||
|
@ -196,7 +210,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_entryListView.setViewMode(_prefs.getCurrentViewMode());
|
||||
_entryListView.setCopyBehavior(_prefs.getCopyBehavior());
|
||||
_entryListView.setSearchBehaviorMask(_prefs.getSearchBehaviorMask());
|
||||
_entryListView.setPrefGroupFilter(_prefs.getGroupFilter());
|
||||
_prefGroupFilter = _prefs.getGroupFilter();
|
||||
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
fab.setOnClickListener(v -> {
|
||||
|
@ -220,10 +234,150 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
Dialogs.showSecureDialog(dialog);
|
||||
});
|
||||
|
||||
_groupChip = findViewById(R.id.groupChipGroup);
|
||||
_fabScrollHelper = new FabScrollHelper(fab);
|
||||
_selectedEntries = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void setGroups(Collection<VaultGroup> groups) {
|
||||
_groups = groups;
|
||||
_groupChip.setVisibility(_groups.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
|
||||
if (_prefGroupFilter != null) {
|
||||
Set<UUID> groupFilter = cleanGroupFilter(_prefGroupFilter);
|
||||
_prefGroupFilter = null;
|
||||
if (!groupFilter.isEmpty()) {
|
||||
_groupFilter = groupFilter;
|
||||
_entryListView.setGroupFilter(groupFilter, false);
|
||||
}
|
||||
} else if (_groupFilter != null) {
|
||||
Set<UUID> groupFilter = cleanGroupFilter(_groupFilter);
|
||||
if (!_groupFilter.equals(groupFilter)) {
|
||||
_groupFilter = groupFilter;
|
||||
_entryListView.setGroupFilter(groupFilter, true);
|
||||
}
|
||||
}
|
||||
|
||||
_entryListView.setGroups(groups);
|
||||
initializeGroups();
|
||||
}
|
||||
|
||||
private void initializeGroups() {
|
||||
_groupChip.removeAllViews();
|
||||
|
||||
addChipTo(_groupChip, new VaultGroupModel(getString(R.string.all)));
|
||||
|
||||
for (VaultGroup group : _groups) {
|
||||
addChipTo(_groupChip, new VaultGroupModel(group));
|
||||
}
|
||||
|
||||
addSaveChip(_groupChip);
|
||||
}
|
||||
|
||||
private Set<UUID> cleanGroupFilter(Set<UUID> groupFilter) {
|
||||
Set<UUID> groupUuids = _groups.stream().map(UUIDMap.Value::getUUID).collect(Collectors.toSet());
|
||||
|
||||
return groupFilter.stream()
|
||||
.filter(g -> g == null || groupUuids.contains(g))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void addChipTo(ChipGroup chipGroup, VaultGroupModel group) {
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_group_filter, null, false);
|
||||
chip.setText(group.getName());
|
||||
chip.setCheckable(true);
|
||||
chip.setCheckedIconVisible(false);
|
||||
chip.setChecked(_groupFilter != null && _groupFilter.contains(group.getUUID()));
|
||||
|
||||
if (group.isPlaceholder()) {
|
||||
chip.setId(0);
|
||||
chip.setTag(null);
|
||||
chip.setChecked(_groupFilter == null);
|
||||
chip.setOnClickListener(v -> {
|
||||
setSaveChipVisibility(true);
|
||||
|
||||
Chip checkedChip = (Chip) chipGroup.getChildAt(0);
|
||||
boolean checkedState = checkedChip.isChecked();
|
||||
chipGroup.clearCheck();
|
||||
|
||||
Set<UUID> groupFilter = getGroupFilter(chipGroup);
|
||||
if (!checkedState) {
|
||||
groupFilter = new HashSet<>();
|
||||
groupFilter.add(null);
|
||||
|
||||
checkedChip.setChecked(false);
|
||||
} else {
|
||||
checkedChip.setChecked(true);
|
||||
}
|
||||
|
||||
_groupFilter = groupFilter;
|
||||
_entryListView.setGroupFilter(groupFilter, true);
|
||||
});
|
||||
|
||||
chipGroup.addView(chip);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
chip.setOnCheckedChangeListener((group1, checkedId) -> {
|
||||
setSaveChipVisibility(true);
|
||||
Set<UUID> groupFilter = getGroupFilter(chipGroup);
|
||||
|
||||
if (groupFilter.isEmpty()) {
|
||||
groupFilter.add(null);
|
||||
} else {
|
||||
Chip allGroupsChip = (Chip) chipGroup.getChildAt(0);
|
||||
allGroupsChip.setChecked(false);
|
||||
}
|
||||
|
||||
_groupFilter = groupFilter;
|
||||
_entryListView.setGroupFilter(groupFilter, true);
|
||||
});
|
||||
|
||||
chip.setTag(group);
|
||||
chipGroup.addView(chip);
|
||||
}
|
||||
|
||||
private void addSaveChip(ChipGroup chipGroup) {
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_group_filter, null, false);
|
||||
|
||||
chip.setText(getString(R.string.save));
|
||||
chip.setVisibility(View.GONE);
|
||||
chip.setChipStrokeWidth(0);
|
||||
chip.setCheckable(false);
|
||||
chip.setChipBackgroundColorResource(android.R.color.transparent);
|
||||
chip.setTextColor(MaterialColors.getColor(chip.getRootView(), com.google.android.material.R.attr.colorSecondary));
|
||||
chip.setClickable(true);
|
||||
chip.setCheckedIconVisible(false);
|
||||
chip.setOnClickListener(v -> {
|
||||
onSaveGroupFilter(_groupFilter);
|
||||
setSaveChipVisibility(false);
|
||||
});
|
||||
|
||||
chipGroup.addView(chip);
|
||||
}
|
||||
|
||||
private void setSaveChipVisibility(boolean visible) {
|
||||
Chip saveChip = (Chip) _groupChip.getChildAt(_groupChip.getChildCount() - 1);
|
||||
saveChip.setChecked(false);
|
||||
saveChip.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private static Set<UUID> getGroupFilter(ChipGroup chipGroup) {
|
||||
return chipGroup.getCheckedChipIds().stream()
|
||||
.map(i -> {
|
||||
Chip chip = chipGroup.findViewById(i);
|
||||
if (chip.getTag() != null) {
|
||||
VaultGroupModel group = (VaultGroupModel) chip.getTag();
|
||||
return group.getUUID();
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
_entryListView.setListener(null);
|
||||
|
@ -252,6 +406,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
instance.putString("submittedSearchQuery", _submittedSearchQuery);
|
||||
instance.putBoolean("isDoingIntro", _isDoingIntro);
|
||||
instance.putBoolean("isAuthenticating", _isAuthenticating);
|
||||
|
||||
if (_groupFilter != null) {
|
||||
instance.putSerializable("prefGroupFilter", new HashSet<>(_groupFilter));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -677,7 +835,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
startAuthActivity(false);
|
||||
} else if (_loaded) {
|
||||
// update the list of groups in the entry list view so that the chip gets updated
|
||||
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
|
||||
setGroups(_vaultManager.getVault().getUsedGroups());
|
||||
|
||||
// update the usage counts in case they are edited outside of the EntryListView
|
||||
_entryListView.setUsageCounts(_prefs.getUsageCounts());
|
||||
|
@ -717,7 +875,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
|
||||
updateLockIcon();
|
||||
if (_loaded) {
|
||||
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
|
||||
setGroups(_vaultManager.getVault().getUsedGroups());
|
||||
updateSortCategoryMenu();
|
||||
}
|
||||
|
||||
|
@ -836,7 +994,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
|
||||
private void loadEntries() {
|
||||
if (!_loaded) {
|
||||
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
|
||||
setGroups(_vaultManager.getVault().getUsedGroups());
|
||||
_entryListView.setUsageCounts(_prefs.getUsageCounts());
|
||||
_entryListView.setLastUsedTimestamps(_prefs.getLastUsedTimestamps());
|
||||
_entryListView.addEntries(_vaultManager.getVault().getEntries());
|
||||
|
@ -930,6 +1088,19 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
Dialogs.showSecureDialog(dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(@Nullable Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
if (savedInstanceState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<UUID> filter = (HashSet<UUID>) savedInstanceState.getSerializable("prefGroupFilter");
|
||||
if (filter != null) {
|
||||
_prefGroupFilter = filter;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntryClick(VaultEntry entry) {
|
||||
if (_actionMode != null) {
|
||||
|
|
|
@ -13,15 +13,12 @@ import android.view.MotionEvent;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.LayoutAnimationController;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
@ -39,10 +36,8 @@ 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.Dialogs;
|
||||
import com.beemdevelopment.aegis.ui.glide.GlideHelper;
|
||||
import com.beemdevelopment.aegis.ui.models.ErrorCardInfo;
|
||||
import com.beemdevelopment.aegis.ui.models.VaultGroupModel;
|
||||
import com.beemdevelopment.aegis.util.UUIDMap;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultGroup;
|
||||
|
@ -51,11 +46,7 @@ import com.bumptech.glide.ListPreloader;
|
|||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader;
|
||||
import com.bumptech.glide.util.ViewPreloadSizeProvider;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.shape.CornerFamily;
|
||||
import com.google.android.material.shape.ShapeAppearanceModel;
|
||||
import com.google.common.base.Strings;
|
||||
|
@ -81,11 +72,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
private TotpProgressBar _progressBar;
|
||||
private boolean _showProgress;
|
||||
private ViewMode _viewMode;
|
||||
private Collection<VaultGroup> _groups;
|
||||
private LinearLayout _emptyStateView;
|
||||
private Chip _groupChip;
|
||||
private Set<UUID> _groupFilter;
|
||||
private Set<UUID> _prefGroupFilter;
|
||||
|
||||
private UiRefresher _refresher;
|
||||
|
||||
|
@ -106,8 +93,6 @@ 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);
|
||||
initializeGroupChip();
|
||||
|
||||
// set up the recycler view
|
||||
_recyclerView = view.findViewById(R.id.rvKeyProfiles);
|
||||
|
@ -173,41 +158,21 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
_preloadSizeProvider.setView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
|
||||
super.onViewStateRestored(savedInstanceState);
|
||||
if (savedInstanceState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<UUID> filter = (HashSet<UUID>) savedInstanceState.getSerializable("prefGroupFilter");
|
||||
if (filter != null) {
|
||||
_prefGroupFilter = filter;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// user can apply _groupFilter without saving
|
||||
// restore _groupFilter as _prefGroupFilter in order to reapply correct filter after screen rotate
|
||||
if (_groupFilter != null) {
|
||||
outState.putSerializable("prefGroupFilter", new HashSet<>(_groupFilter));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
_refresher.destroy();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
public void setGroups(Collection<VaultGroup> groups) {
|
||||
_adapter.setGroups(groups);
|
||||
updateDividerDecoration();
|
||||
}
|
||||
|
||||
public void setGroupFilter(Set<UUID> groups, boolean animate) {
|
||||
_groupFilter = groups;
|
||||
_adapter.setGroupFilter(groups);
|
||||
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());
|
||||
updateEmptyState();
|
||||
updateGroupChip();
|
||||
|
||||
if (animate) {
|
||||
runEntriesAnimation();
|
||||
|
@ -384,10 +349,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
}
|
||||
}
|
||||
|
||||
public void setPrefGroupFilter(Set<UUID> groupFilter) {
|
||||
_prefGroupFilter = groupFilter;
|
||||
}
|
||||
|
||||
public void setCodeGroupSize(Preferences.CodeGrouping codeGrouping) {
|
||||
_adapter.setCodeGroupSize(codeGrouping);
|
||||
}
|
||||
|
@ -519,116 +480,11 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
_recyclerView.scheduleLayoutAnimation();
|
||||
}
|
||||
|
||||
private void addChipTo(ChipGroup chipGroup, VaultGroupModel group) {
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_group_filter, null, false);
|
||||
chip.setText(group.getName());
|
||||
chip.setCheckable(true);
|
||||
chip.setChecked(_groupFilter != null && _groupFilter.contains(group.getUUID()));
|
||||
chip.setCheckedIconVisible(true);
|
||||
chip.setOnCheckedChangeListener((group1, checkedId) -> {
|
||||
Set<UUID> groupFilter = getGroupFilter(chipGroup);
|
||||
setGroupFilter(groupFilter, true);
|
||||
});
|
||||
chip.setTag(group);
|
||||
chipGroup.addView(chip);
|
||||
}
|
||||
|
||||
private void initializeGroupChip() {
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_select_groups, null);
|
||||
BottomSheetDialog dialog = new BottomSheetDialog(requireContext());
|
||||
NestedScrollView scrollView = view.findViewById(R.id.scrollView);
|
||||
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) scrollView.getLayoutParams();
|
||||
layoutParams.matchConstraintMaxHeight = getResources().getConfiguration().screenHeightDp;
|
||||
|
||||
dialog.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
dialog.getBehavior().setSkipCollapsed(false);
|
||||
dialog.setContentView(view);
|
||||
|
||||
ChipGroup chipGroup = view.findViewById(R.id.groupChipGroup);
|
||||
Button clearButton = view.findViewById(R.id.btnClear);
|
||||
Button saveButton = view.findViewById(R.id.btnSave);
|
||||
clearButton.setOnClickListener(v -> {
|
||||
chipGroup.clearCheck();
|
||||
Set<UUID> groupFilter = Collections.emptySet();
|
||||
if (_listener != null) {
|
||||
_listener.onSaveGroupFilter(groupFilter);
|
||||
}
|
||||
setGroupFilter(groupFilter, true);
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
saveButton.setOnClickListener(v -> {
|
||||
Set<UUID> groupFilter = getGroupFilter(chipGroup);
|
||||
if (_listener != null) {
|
||||
_listener.onSaveGroupFilter(groupFilter);
|
||||
}
|
||||
setGroupFilter(groupFilter, true);
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
_groupChip.setOnClickListener(v -> {
|
||||
chipGroup.removeAllViews();
|
||||
|
||||
for (VaultGroup group : _groups) {
|
||||
addChipTo(chipGroup, new VaultGroupModel(group));
|
||||
}
|
||||
addChipTo(chipGroup, new VaultGroupModel(getString(R.string.no_group)));
|
||||
|
||||
Dialogs.showSecureDialog(dialog);
|
||||
});
|
||||
}
|
||||
|
||||
private static Set<UUID> getGroupFilter(ChipGroup chipGroup) {
|
||||
return chipGroup.getCheckedChipIds().stream()
|
||||
.map(i -> {
|
||||
Chip chip = chipGroup.findViewById(i);
|
||||
VaultGroupModel group = (VaultGroupModel) chip.getTag();
|
||||
return group.getUUID();
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void updateGroupChip() {
|
||||
if (_groupFilter.isEmpty()) {
|
||||
_groupChip.setText(R.string.groups);
|
||||
} else {
|
||||
_groupChip.setText(String.format("%s (%d)", getString(R.string.groups), _groupFilter.size()));
|
||||
}
|
||||
}
|
||||
|
||||
private void setShowProgress(boolean showProgress) {
|
||||
_showProgress = showProgress;
|
||||
updateDividerDecoration();
|
||||
}
|
||||
|
||||
public void setGroups(Collection<VaultGroup> groups) {
|
||||
_groups = groups;
|
||||
_adapter.setGroups(groups);
|
||||
_groupChip.setVisibility(_groups.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
updateDividerDecoration();
|
||||
|
||||
if (_prefGroupFilter != null) {
|
||||
Set<UUID> groupFilter = cleanGroupFilter(_prefGroupFilter);
|
||||
_prefGroupFilter = null;
|
||||
if (!groupFilter.isEmpty()) {
|
||||
setGroupFilter(groupFilter, false);
|
||||
}
|
||||
} else if (_groupFilter != null) {
|
||||
Set<UUID> groupFilter = cleanGroupFilter(_groupFilter);
|
||||
if (!_groupFilter.equals(groupFilter)) {
|
||||
setGroupFilter(groupFilter, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<UUID> cleanGroupFilter(Set<UUID> groupFilter) {
|
||||
Set<UUID> groupUuids = _groups.stream().map(UUIDMap.Value::getUUID).collect(Collectors.toSet());
|
||||
|
||||
return groupFilter.stream()
|
||||
.filter(g -> g == null || groupUuids.contains(g))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void updateDividerDecoration() {
|
||||
if (_itemDecoration != null) {
|
||||
_recyclerView.removeItemDecoration(_itemDecoration);
|
||||
|
@ -705,8 +561,8 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
}
|
||||
|
||||
int entryIndex = _adapter.translateEntryPosToIndex(adapterPosition);
|
||||
// The first entry should have a top margin, but only if the group chip is not shown and the error card is not shown
|
||||
if (entryIndex == 0 && (_groups == null || _groups.isEmpty()) && !_adapter.isErrorCardShown()) {
|
||||
// The first entry should have a top margin, but only if the error card is not shown
|
||||
if (entryIndex == 0 && !_adapter.isErrorCardShown()) {
|
||||
outRect.top = _offset;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
|
||||
|
@ -15,30 +14,47 @@
|
|||
android:fitsSystemWindows="true"
|
||||
app:liftOnScroll="true"
|
||||
app:liftOnScrollTargetViewId="@+id/rvKeyProfiles">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:paddingTop="0dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/groupChipGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"></com.google.android.material.chip.ChipGroup>
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/groupChipGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<fragment
|
||||
android:name="com.beemdevelopment.aegis.ui.views.EntryListView"
|
||||
android:id="@+id/key_profiles"
|
||||
android:layout_height="fill_parent"
|
||||
android:name="com.beemdevelopment.aegis.ui.views.EntryListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -48,6 +64,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
android:src="@drawable/ic_outline_add_24" />
|
||||
android:src="@drawable/ic_outline_add_24" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/groupChipGroup"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
|
|
@ -14,21 +14,6 @@
|
|||
android:visibility="gone"
|
||||
android:max="5000"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/groups"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:visibility="gone"
|
||||
app:chipEndPadding="6dp"
|
||||
app:chipIcon="@drawable/ic_outline_filter_list_24"
|
||||
app:chipIconTint="?attr/colorOnSurface"
|
||||
app:chipIconSize="18dp"
|
||||
app:chipStrokeWidth="1dp"
|
||||
app:iconStartPadding="6dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<string name="action_default_icon">Restore default icon</string>
|
||||
<string name="discard">Discard</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="all">All</string>
|
||||
<string name="issuer">Issuer</string>
|
||||
<string name="yandex_pin">PIN (4–16 digits)</string>
|
||||
<string name="motp_pin">PIN (4 digits)</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue