Skip to content

Commit

Permalink
Refer to groups by UUID
Browse files Browse the repository at this point in the history
 - Also lays the foundations for adding entries to multiple groups and changing group names

Co-authored-by: Alexander Bakker <ab@alexbakker.me>
  • Loading branch information
orange-elephant and alexbakker committed Sep 11, 2023
1 parent b84ecf1 commit c346507
Show file tree
Hide file tree
Showing 22 changed files with 594 additions and 197 deletions.
18 changes: 10 additions & 8 deletions app/src/main/java/com/beemdevelopment/aegis/Preferences.java
Expand Up @@ -20,9 +20,11 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public class Preferences {
Expand Down Expand Up @@ -478,26 +480,26 @@ public boolean isMinimizeOnCopyEnabled() {
return _prefs.getBoolean("pref_minimize_on_copy", false);
}

public void setGroupFilter(List<String> groupFilter) {
public void setGroupFilter(Set<UUID> groupFilter) {
JSONArray json = new JSONArray(groupFilter);
_prefs.edit().putString("pref_group_filter", json.toString()).apply();
_prefs.edit().putString("pref_group_filter_uuids", json.toString()).apply();
}

public List<String> getGroupFilter() {
String raw = _prefs.getString("pref_group_filter", null);
public Set<UUID> getGroupFilter() {
String raw = _prefs.getString("pref_group_filter_uuids", null);
if (raw == null || raw.isEmpty()) {
return Collections.emptyList();
return Collections.emptySet();
}

try {
JSONArray json = new JSONArray(raw);
List<String> filter = new ArrayList<>();
Set<UUID> filter = new HashSet<>();
for (int i = 0; i < json.length(); i++) {
filter.add(json.isNull(i) ? null : json.optString(i));
filter.add(json.isNull(i) ? null : UUID.fromString(json.getString(i)));
}
return filter;
} catch (JSONException e) {
return Collections.emptyList();
return Collections.emptySet();
}
}

Expand Down
Expand Up @@ -16,6 +16,7 @@
import com.beemdevelopment.aegis.vault.VaultFile;
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
import com.beemdevelopment.aegis.vault.VaultFileException;
import com.beemdevelopment.aegis.vault.VaultGroup;
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
import com.beemdevelopment.aegis.vault.slots.SlotList;
import com.topjohnwu.superuser.io.SuFile;
Expand All @@ -27,6 +28,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

public class AegisImporter extends DatabaseImporter {

Expand Down Expand Up @@ -132,11 +134,31 @@ public Result convert() throws DatabaseImporterException {
Result result = new Result();

try {
JSONArray array = _obj.getJSONArray("entries");
for (int i = 0; i < array.length(); i++) {
JSONObject entryObj = array.getJSONObject(i);
if (_obj.has("groups")) {
JSONArray groupArray = _obj.getJSONArray("groups");
for (int i = 0; i < groupArray.length(); i++) {
JSONObject groupObj = groupArray.getJSONObject(i);
try {
VaultGroup group = convertGroup(groupObj);
if (!result.getGroups().has(group)) {
result.addGroup(group);
}
} catch (DatabaseImporterEntryException e) {
result.addError(e);
}
}
}

JSONArray entryArray = _obj.getJSONArray("entries");
for (int i = 0; i < entryArray.length(); i++) {
JSONObject entryObj = entryArray.getJSONObject(i);
try {
VaultEntry entry = convertEntry(entryObj);
for (UUID groupUuid : entry.getGroups()) {
if (!result.getGroups().has(groupUuid)) {
entry.getGroups().remove(groupUuid);
}
}
result.addEntry(entry);
} catch (DatabaseImporterEntryException e) {
result.addError(e);
Expand All @@ -156,5 +178,13 @@ private static VaultEntry convertEntry(JSONObject obj) throws DatabaseImporterEn
throw new DatabaseImporterEntryException(e, obj.toString());
}
}

private static VaultGroup convertGroup(JSONObject obj) throws DatabaseImporterEntryException {
try {
return VaultGroup.fromJson(obj);
} catch (VaultEntryException e) {
throw new DatabaseImporterEntryException(e, obj.toString());
}
}
}
}
Expand Up @@ -8,6 +8,7 @@
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultGroup;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
Expand Down Expand Up @@ -168,12 +169,17 @@ public Result convert() throws DatabaseImporterException {

public static class Result {
private UUIDMap<VaultEntry> _entries = new UUIDMap<>();
private UUIDMap<VaultGroup> _groups = new UUIDMap<>();
private List<DatabaseImporterEntryException> _errors = new ArrayList<>();

public void addEntry(VaultEntry entry) {
_entries.add(entry);
}

public void addGroup(VaultGroup group) {
_groups.add(group);
}

public void addError(DatabaseImporterEntryException error) {
_errors.add(error);
}
Expand All @@ -182,6 +188,10 @@ public UUIDMap<VaultEntry> getEntries() {
return _entries;
}

public UUIDMap<VaultGroup> getGroups() {
return _groups;
}

public List<DatabaseImporterEntryException> getErrors() {
return _errors;
}
Expand Down
@@ -1,7 +1,7 @@
package com.beemdevelopment.aegis.ui;

import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
Expand All @@ -15,7 +15,6 @@
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.AdapterView;
import android.widget.AutoCompleteTextView;
import android.widget.ImageView;
import android.widget.LinearLayout;
Expand Down Expand Up @@ -54,11 +53,13 @@
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import com.beemdevelopment.aegis.ui.dialogs.IconPickerDialog;
import com.beemdevelopment.aegis.ui.glide.IconLoader;
import com.beemdevelopment.aegis.ui.models.VaultGroupModel;
import com.beemdevelopment.aegis.ui.tasks.ImportFileTask;
import com.beemdevelopment.aegis.ui.views.IconAdapter;
import com.beemdevelopment.aegis.util.Cloner;
import com.beemdevelopment.aegis.util.IOUtils;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultGroup;
import com.beemdevelopment.aegis.vault.VaultRepository;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
Expand All @@ -73,11 +74,14 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.TreeSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
Expand All @@ -90,7 +94,7 @@ public class EditEntryActivity extends AegisActivity {
private boolean _isNew = false;
private boolean _isManual = false;
private VaultEntry _origEntry;
private TreeSet<String> _groups;
private Collection<VaultGroup> _groups;
private boolean _hasCustomIcon = false;
// keep track of icon changes separately as the generated jpeg's are not deterministic
private boolean _hasChangedIcon = false;
Expand All @@ -114,7 +118,7 @@ public class EditEntryActivity extends AegisActivity {
private AutoCompleteTextView _dropdownAlgo;
private TextInputLayout _dropdownAlgoLayout;
private AutoCompleteTextView _dropdownGroup;
private List<String> _dropdownGroupList = new ArrayList<>();
private List<VaultGroupModel> _dropdownGroupList = new ArrayList<>();

private KropView _kropView;

Expand Down Expand Up @@ -262,8 +266,13 @@ protected void onCreate(Bundle savedInstanceState) {
updateAdvancedFieldStatus(_origEntry.getInfo().getTypeId());
updatePinFieldVisibility(_origEntry.getInfo().getTypeId());

String group = _origEntry.getGroup();
setGroup(group);
Set<UUID> groups = _origEntry.getGroups();
if (groups.isEmpty()) {
setGroup(new VaultGroupModel(getString(R.string.no_group)));
} else {
VaultGroup group = _vaultManager.getVault().getGroupByUUID(groups.iterator().next());
setGroup(new VaultGroupModel(group));
}

// Update the icon if the issuer or name has changed
_textIssuer.addTextChangedListener(_nameChangeListener);
Expand Down Expand Up @@ -327,24 +336,31 @@ protected void onCreate(Bundle savedInstanceState) {
startIconSelection();
});

_dropdownGroup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
private int prevPosition = _dropdownGroupList.indexOf(_dropdownGroup.getText().toString());

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == _dropdownGroupList.size() - 1) {
Dialogs.showTextInputDialog(EditEntryActivity.this, R.string.set_group, R.string.group_name_hint, text -> {
String groupName = new String(text);
if (!groupName.isEmpty()) {
_groups.add(groupName);
updateGroupDropdownList();
_dropdownGroup.setText(groupName, false);
_dropdownGroup.setOnItemClickListener((parent, view, position, id) -> {
VaultGroupModel selectedGroup = _dropdownGroupList.get(position);
if (selectedGroup.isPlaceholder() && Objects.equals(selectedGroup.getName(), getString(R.string.new_group))) {
Dialogs.TextInputListener onAddGroup = text -> {
String groupName = new String(text).trim();
if (!groupName.isEmpty()) {
VaultGroup group = _vaultManager.getVault().findGroupByName(groupName);
if (group == null) {
group = new VaultGroup(groupName);
_vaultManager.getVault().addGroup(group);
}
});
_dropdownGroup.setText(_dropdownGroupList.get(prevPosition), false);
} else {
prevPosition = position;
}

updateGroupDropdownList();
setGroup(new VaultGroupModel(group));
}
};

DialogInterface.OnCancelListener onCancel = dialogInterface -> {
VaultGroupModel previous = (VaultGroupModel) _dropdownGroup.getTag();
_dropdownGroup.setText(previous.getName(), false);
};

Dialogs.showTextInputDialog(EditEntryActivity.this, R.string.set_group, R.string.group_name_hint, onAddGroup, onCancel);
} else {
setGroup(_dropdownGroupList.get(position));
}
});

Expand All @@ -365,13 +381,9 @@ private void updatePinFieldVisibility(String otpType) {
_textPin.setHint(otpType.equals(MotpInfo.ID) ? R.string.motp_pin : R.string.yandex_pin);
}

private void setGroup(String groupName) {
int pos = 0;
if (groupName != null) {
pos = _groups.contains(groupName) ? _groups.headSet(groupName).size() + 1 : 0;
}

_dropdownGroup.setText(_dropdownGroupList.get(pos), false);
private void setGroup(VaultGroupModel group) {
_dropdownGroup.setText(group.getName(), false);
_dropdownGroup.setTag(group);
}

private void openAdvancedSettings() {
Expand All @@ -395,11 +407,10 @@ private void openAdvancedSettings() {
}

private void updateGroupDropdownList() {
Resources res = getResources();
_dropdownGroupList.clear();
_dropdownGroupList.add(res.getString(R.string.no_group));
_dropdownGroupList.addAll(_groups);
_dropdownGroupList.add(res.getString(R.string.new_group));
_dropdownGroupList.add(new VaultGroupModel(getString(R.string.new_group)));
_dropdownGroupList.addAll(_groups.stream().map(VaultGroupModel::new).collect(Collectors.toList()));
_dropdownGroupList.add(new VaultGroupModel(getString(R.string.no_group)));
}

private boolean hasUnsavedChanges(VaultEntry newEntry) {
Expand Down Expand Up @@ -726,12 +737,13 @@ private VaultEntry parseEntry() throws ParseException {
entry.setName(_textName.getText().toString());
entry.setNote(_textNote.getText().toString());

int groupPos = _dropdownGroupList.indexOf(_dropdownGroup.getText().toString());
if (groupPos != 0) {
String group = _dropdownGroupList.get(groupPos);
entry.setGroup(group);
VaultGroupModel group = (VaultGroupModel) _dropdownGroup.getTag();
if (group.isPlaceholder()) {
entry.setGroups(new HashSet<>());
} else {
entry.setGroup(null);
Set<UUID> groups = new HashSet<>();
groups.add(group.getUUID());
entry.setGroups(groups);
}

if (_hasChangedIcon) {
Expand Down

0 comments on commit c346507

Please sign in to comment.