mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-15 14:32:49 +00:00
Add ability to skip duplicates during import
This commit is contained in:
parent
5e35cc203f
commit
b205438982
3 changed files with 55 additions and 1 deletions
|
@ -4,6 +4,7 @@ import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -26,15 +27,20 @@ import com.beemdevelopment.aegis.util.UUIDMap;
|
||||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||||
import com.beemdevelopment.aegis.vault.VaultRepository;
|
import com.beemdevelopment.aegis.vault.VaultRepository;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ImportEntriesActivity extends AegisActivity {
|
public class ImportEntriesActivity extends AegisActivity {
|
||||||
|
private View _view;
|
||||||
private Menu _menu;
|
private Menu _menu;
|
||||||
private ImportEntriesAdapter _adapter;
|
private ImportEntriesAdapter _adapter;
|
||||||
private FabScrollHelper _fabScrollHelper;
|
private FabScrollHelper _fabScrollHelper;
|
||||||
|
@ -48,6 +54,8 @@ public class ImportEntriesActivity extends AegisActivity {
|
||||||
setContentView(R.layout.activity_import_entries);
|
setContentView(R.layout.activity_import_entries);
|
||||||
setSupportActionBar(findViewById(R.id.toolbar));
|
setSupportActionBar(findViewById(R.id.toolbar));
|
||||||
|
|
||||||
|
_view = findViewById(R.id.importEntriesRootView);
|
||||||
|
|
||||||
ActionBar bar = getSupportActionBar();
|
ActionBar bar = getSupportActionBar();
|
||||||
bar.setHomeAsUpIndicator(R.drawable.ic_close);
|
bar.setHomeAsUpIndicator(R.drawable.ic_close);
|
||||||
bar.setDisplayHomeAsUpEnabled(true);
|
bar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
@ -180,6 +188,7 @@ public class ImportEntriesActivity extends AegisActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importDatabase(DatabaseImporter.State state) {
|
private void importDatabase(DatabaseImporter.State state) {
|
||||||
|
List<ImportEntry> importEntries = new ArrayList<>();
|
||||||
DatabaseImporter.Result result;
|
DatabaseImporter.Result result;
|
||||||
try {
|
try {
|
||||||
result = state.convert();
|
result = state.convert();
|
||||||
|
@ -191,7 +200,9 @@ public class ImportEntriesActivity extends AegisActivity {
|
||||||
|
|
||||||
UUIDMap<VaultEntry> entries = result.getEntries();
|
UUIDMap<VaultEntry> entries = result.getEntries();
|
||||||
for (VaultEntry entry : entries.getValues()) {
|
for (VaultEntry entry : entries.getValues()) {
|
||||||
_adapter.addEntry(new ImportEntry(entry));
|
ImportEntry importEntry = new ImportEntry(entry);
|
||||||
|
_adapter.addEntry(importEntry);
|
||||||
|
importEntries.add(importEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DatabaseImporterEntryException> errors = result.getErrors();
|
List<DatabaseImporterEntryException> errors = result.getErrors();
|
||||||
|
@ -199,6 +210,8 @@ public class ImportEntriesActivity extends AegisActivity {
|
||||||
String message = getResources().getQuantityString(R.plurals.import_error_dialog, errors.size(), errors.size());
|
String message = getResources().getQuantityString(R.plurals.import_error_dialog, errors.size(), errors.size());
|
||||||
Dialogs.showMultiErrorDialog(this, R.string.import_error_title, message, errors, null);
|
Dialogs.showMultiErrorDialog(this, R.string.import_error_title, message, errors, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findDuplicates(importEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showWipeEntriesDialog() {
|
private void showWipeEntriesDialog() {
|
||||||
|
@ -236,6 +249,33 @@ public class ImportEntriesActivity extends AegisActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void findDuplicates(List<ImportEntry> importEntries) {
|
||||||
|
List<UUID> duplicateEntries = new ArrayList<>();
|
||||||
|
for (ImportEntry importEntry: importEntries) {
|
||||||
|
boolean exists = _vaultManager.getVault().getEntries().stream().anyMatch(item ->
|
||||||
|
item.getIssuer().equals(importEntry.getEntry().getIssuer()) &&
|
||||||
|
Arrays.equals(item.getInfo().getSecret(), importEntry.getEntry().getInfo().getSecret()));
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
duplicateEntries.add(importEntry.getEntry().getUUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateEntries.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_adapter.setCheckboxStates(duplicateEntries, false);
|
||||||
|
Snackbar snackbar = Snackbar.make(_view, getResources().getQuantityString(R.plurals.import_duplicate_toast, duplicateEntries.size(), duplicateEntries.size()), Snackbar.LENGTH_INDEFINITE);
|
||||||
|
snackbar.setAction(R.string.undo, new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
_adapter.setCheckboxStates(duplicateEntries, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
snackbar.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
_menu = menu;
|
_menu = menu;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.beemdevelopment.aegis.ui.models.ImportEntry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ImportEntriesAdapter extends RecyclerView.Adapter<ImportEntryHolder> {
|
public class ImportEntriesAdapter extends RecyclerView.Adapter<ImportEntryHolder> {
|
||||||
private List<ImportEntry> _entries;
|
private List<ImportEntry> _entries;
|
||||||
|
@ -67,6 +68,14 @@ public class ImportEntriesAdapter extends RecyclerView.Adapter<ImportEntryHolder
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCheckboxStates(List<UUID> uuids, boolean state) {
|
||||||
|
for (ImportEntry entry : _entries) {
|
||||||
|
if(uuids.contains(entry.getEntry().getUUID())) {
|
||||||
|
entry.setIsChecked(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void toggleCheckboxes() {
|
public void toggleCheckboxes() {
|
||||||
int checkedEntries = getCheckedEntries().size();
|
int checkedEntries = getCheckedEntries().size();
|
||||||
if (checkedEntries == 0 || checkedEntries != _entries.size()) {
|
if (checkedEntries == 0 || checkedEntries != _entries.size()) {
|
||||||
|
|
|
@ -159,6 +159,7 @@
|
||||||
<string name="share">Share</string>
|
<string name="share">Share</string>
|
||||||
<string name="yes">Yes</string>
|
<string name="yes">Yes</string>
|
||||||
<string name="no">No</string>
|
<string name="no">No</string>
|
||||||
|
<string name="undo">Undo</string>
|
||||||
<string name="unlock">Unlock</string>
|
<string name="unlock">Unlock</string>
|
||||||
<string name="advanced">Advanced</string>
|
<string name="advanced">Advanced</string>
|
||||||
<string name="counter">Counter</string>
|
<string name="counter">Counter</string>
|
||||||
|
@ -469,6 +470,10 @@
|
||||||
<string name="pref_panic_trigger_summary">Delete vault when a panic trigger is received from Ripple</string>
|
<string name="pref_panic_trigger_summary">Delete vault when a panic trigger is received from Ripple</string>
|
||||||
|
|
||||||
<string name="import_vault">Import vault</string>
|
<string name="import_vault">Import vault</string>
|
||||||
|
<plurals name="import_duplicate_toast">
|
||||||
|
<item quantity="one">Unchecked %d potential duplicate. Please review the list of entries.</item>
|
||||||
|
<item quantity="other">Unchecked %d potential duplicates. Please review the list of entries.</item>
|
||||||
|
</plurals>
|
||||||
<string name="importer_help_2fas">Supply a 2FAS Authenticator backup file.</string>
|
<string name="importer_help_2fas">Supply a 2FAS Authenticator backup file.</string>
|
||||||
<string name="importer_help_aegis">Supply an Aegis export/backup file.</string>
|
<string name="importer_help_aegis">Supply an Aegis export/backup file.</string>
|
||||||
<string name="importer_help_authenticator_plus">Supply an Authenticator Plus export file obtained through <b>Settings -> Backup & Restore -> Export as Text and HTML</b>.</string>
|
<string name="importer_help_authenticator_plus">Supply an Authenticator Plus export file obtained through <b>Settings -> Backup & Restore -> Export as Text and HTML</b>.</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue