Skip to content

Commit

Permalink
Improve group filters
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelschattgen committed Sep 16, 2024
1 parent 0046e88 commit 9c151d8
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,10 @@ private void changeSort(@IdRes int resId) {
}

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());
}
}

Expand Down
179 changes: 175 additions & 4 deletions app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand All @@ -50,21 +51,29 @@
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;
Expand All @@ -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;
Expand Down Expand Up @@ -196,7 +210,7 @@ protected void onCreate(Bundle savedInstanceState) {
_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 -> {
Expand All @@ -220,10 +234,150 @@ protected void onCreate(Bundle savedInstanceState) {
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);
Expand Down Expand Up @@ -252,6 +406,10 @@ protected void onSaveInstanceState(@NonNull Bundle instance) {
instance.putString("submittedSearchQuery", _submittedSearchQuery);
instance.putBoolean("isDoingIntro", _isDoingIntro);
instance.putBoolean("isAuthenticating", _isAuthenticating);

if (_groupFilter != null) {
instance.putSerializable("prefGroupFilter", new HashSet<>(_groupFilter));
}
}

@Override
Expand Down Expand Up @@ -677,7 +835,7 @@ protected void onStart() {
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());
Expand Down Expand Up @@ -717,7 +875,7 @@ public boolean onCreateOptionsMenu(Menu menu) {

updateLockIcon();
if (_loaded) {
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
setGroups(_vaultManager.getVault().getUsedGroups());
updateSortCategoryMenu();
}

Expand Down Expand Up @@ -836,7 +994,7 @@ private void collapseSearchView() {

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());
Expand Down Expand Up @@ -930,6 +1088,19 @@ private void showPlaintextExportWarningOptions() {
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) {
Expand Down
Loading

0 comments on commit 9c151d8

Please sign in to comment.