mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-14 14:02:49 +00:00
Continue importing entries even if one can't be parsed
This commit is contained in:
parent
fc0e1150f6
commit
592c6683c3
12 changed files with 253 additions and 112 deletions
|
@ -2,16 +2,16 @@ package com.beemdevelopment.aegis.importers;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.beemdevelopment.aegis.db.Database;
|
|
||||||
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||||
import com.beemdevelopment.aegis.db.DatabaseException;
|
|
||||||
import com.beemdevelopment.aegis.db.DatabaseFile;
|
import com.beemdevelopment.aegis.db.DatabaseFile;
|
||||||
import com.beemdevelopment.aegis.db.DatabaseFileCredentials;
|
import com.beemdevelopment.aegis.db.DatabaseFileCredentials;
|
||||||
import com.beemdevelopment.aegis.db.DatabaseFileException;
|
import com.beemdevelopment.aegis.db.DatabaseFileException;
|
||||||
|
import com.beemdevelopment.aegis.encoding.Base64Exception;
|
||||||
|
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||||
import com.beemdevelopment.aegis.util.ByteInputStream;
|
import com.beemdevelopment.aegis.util.ByteInputStream;
|
||||||
|
|
||||||
public class AegisFileImporter extends DatabaseFileImporter {
|
public class AegisFileImporter extends DatabaseFileImporter {
|
||||||
|
@ -33,7 +33,9 @@ public class AegisFileImporter extends DatabaseFileImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
public DatabaseImporterResult convert() throws DatabaseImporterException {
|
||||||
|
DatabaseImporterResult result = new DatabaseImporterResult();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JSONObject obj;
|
JSONObject obj;
|
||||||
if (_file.isEncrypted() && _creds != null) {
|
if (_file.isEncrypted() && _creds != null) {
|
||||||
|
@ -42,11 +44,29 @@ public class AegisFileImporter extends DatabaseFileImporter {
|
||||||
obj = _file.getContent();
|
obj = _file.getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
Database db = Database.fromJson(obj);
|
JSONArray array = obj.getJSONArray("entries");
|
||||||
return db.getEntries();
|
for (int i = 0; i < array.length(); i++) {
|
||||||
} catch (DatabaseException | DatabaseFileException e) {
|
JSONObject entryObj = array.getJSONObject(i);
|
||||||
|
try {
|
||||||
|
DatabaseEntry entry = convertEntry(entryObj);
|
||||||
|
result.addEntry(entry);
|
||||||
|
} catch (DatabaseImporterEntryException e) {
|
||||||
|
result.addError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException | DatabaseFileException e) {
|
||||||
throw new DatabaseImporterException(e);
|
throw new DatabaseImporterException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DatabaseEntry convertEntry(JSONObject obj) throws DatabaseImporterEntryException {
|
||||||
|
try {
|
||||||
|
return DatabaseEntry.fromJson(obj);
|
||||||
|
} catch (JSONException | OtpInfoException | Base64Exception e) {
|
||||||
|
throw new DatabaseImporterEntryException(e, obj.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,8 +7,6 @@ import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||||
import com.beemdevelopment.aegis.encoding.Base32;
|
import com.beemdevelopment.aegis.encoding.Base32;
|
||||||
|
@ -36,46 +34,55 @@ public class AndOtpFileImporter extends DatabaseFileImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
public DatabaseImporterResult convert() throws DatabaseImporterException {
|
||||||
List<DatabaseEntry> entries = new ArrayList<>();
|
DatabaseImporterResult result = new DatabaseImporterResult();
|
||||||
|
|
||||||
try {
|
for (int i = 0; i < _obj.length(); i++) {
|
||||||
for (int i = 0; i < _obj.length(); i++) {
|
try {
|
||||||
JSONObject obj = _obj.getJSONObject(i);
|
JSONObject obj = _obj.getJSONObject(i);
|
||||||
|
DatabaseEntry entry = convertEntry(obj);
|
||||||
String type = obj.getString("type").toLowerCase();
|
result.addEntry(entry);
|
||||||
String algo = obj.getString("algorithm");
|
} catch (JSONException e) {
|
||||||
int digits = obj.getInt("digits");
|
throw new DatabaseImporterException(e);
|
||||||
byte[] secret = Base32.decode(obj.getString("secret").toCharArray());
|
} catch (DatabaseImporterEntryException e) {
|
||||||
|
result.addError(e);
|
||||||
OtpInfo info;
|
|
||||||
if (type.equals("totp")) {
|
|
||||||
info = new TotpInfo(secret, algo, digits, obj.getInt("period"));
|
|
||||||
} else if (type.equals("hotp")) {
|
|
||||||
info = new HotpInfo(secret, algo, digits, obj.getLong("counter"));
|
|
||||||
} else {
|
|
||||||
throw new DatabaseImporterException("unsupported otp type: " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
String issuer = "";
|
|
||||||
String name = "";
|
|
||||||
|
|
||||||
String[] parts = obj.getString("label").split(" - ");
|
|
||||||
if (parts.length > 1) {
|
|
||||||
issuer = parts[0];
|
|
||||||
name = parts[1];
|
|
||||||
} else {
|
|
||||||
name = parts[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
DatabaseEntry entry = new DatabaseEntry(info, name, issuer);
|
|
||||||
entries.add(entry);
|
|
||||||
}
|
}
|
||||||
} catch (Base32Exception | OtpInfoException | JSONException e) {
|
|
||||||
throw new DatabaseImporterException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DatabaseEntry convertEntry(JSONObject obj) throws DatabaseImporterEntryException {
|
||||||
|
try {
|
||||||
|
String type = obj.getString("type").toLowerCase();
|
||||||
|
String algo = obj.getString("algorithm");
|
||||||
|
int digits = obj.getInt("digits");
|
||||||
|
byte[] secret = Base32.decode(obj.getString("secret").toCharArray());
|
||||||
|
|
||||||
|
OtpInfo info;
|
||||||
|
if (type.equals("totp")) {
|
||||||
|
info = new TotpInfo(secret, algo, digits, obj.getInt("period"));
|
||||||
|
} else if (type.equals("hotp")) {
|
||||||
|
info = new HotpInfo(secret, algo, digits, obj.getLong("counter"));
|
||||||
|
} else {
|
||||||
|
throw new DatabaseImporterException("unsupported otp type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String issuer = "";
|
||||||
|
|
||||||
|
String[] parts = obj.getString("label").split(" - ");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
issuer = parts[0];
|
||||||
|
name = parts[1];
|
||||||
|
} else {
|
||||||
|
name = parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DatabaseEntry(info, name, issuer);
|
||||||
|
} catch (DatabaseImporterException | Base32Exception | OtpInfoException | JSONException e) {
|
||||||
|
throw new DatabaseImporterEntryException(e, obj.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,7 +27,7 @@ public abstract class DatabaseAppImporter implements DatabaseImporter {
|
||||||
|
|
||||||
public abstract void parse() throws DatabaseImporterException;
|
public abstract void parse() throws DatabaseImporterException;
|
||||||
|
|
||||||
public abstract List<DatabaseEntry> convert() throws DatabaseImporterException;
|
public abstract DatabaseImporterResult convert() throws DatabaseImporterException;
|
||||||
|
|
||||||
public abstract boolean isEncrypted();
|
public abstract boolean isEncrypted();
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ public abstract class DatabaseFileImporter implements DatabaseImporter {
|
||||||
|
|
||||||
public abstract void parse() throws DatabaseImporterException;
|
public abstract void parse() throws DatabaseImporterException;
|
||||||
|
|
||||||
public abstract List<DatabaseEntry> convert() throws DatabaseImporterException;
|
public abstract DatabaseImporterResult convert() throws DatabaseImporterException;
|
||||||
|
|
||||||
public abstract boolean isEncrypted();
|
public abstract boolean isEncrypted();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||||
|
|
||||||
public interface DatabaseImporter {
|
public interface DatabaseImporter {
|
||||||
void parse() throws DatabaseImporterException;
|
void parse() throws DatabaseImporterException;
|
||||||
List<DatabaseEntry> convert() throws DatabaseImporterException;
|
DatabaseImporterResult convert() throws DatabaseImporterException;
|
||||||
boolean isEncrypted();
|
boolean isEncrypted();
|
||||||
Context getContext();
|
Context getContext();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.beemdevelopment.aegis.importers;
|
||||||
|
|
||||||
|
public class DatabaseImporterEntryException extends Exception {
|
||||||
|
private String _text;
|
||||||
|
|
||||||
|
public DatabaseImporterEntryException(Throwable cause, String text) {
|
||||||
|
super(cause);
|
||||||
|
_text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return _text;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.beemdevelopment.aegis.importers;
|
||||||
|
|
||||||
|
import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DatabaseImporterResult {
|
||||||
|
private List<DatabaseEntry> _entries = new ArrayList<>();
|
||||||
|
private List<DatabaseImporterEntryException> _errors = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addEntry(DatabaseEntry entry) {
|
||||||
|
_entries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(DatabaseImporterEntryException error) {
|
||||||
|
_errors.add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DatabaseEntry> getEntries() {
|
||||||
|
return _entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DatabaseImporterEntryException> getErrors() {
|
||||||
|
return _errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,43 +46,48 @@ public class FreeOtpFileImporter extends DatabaseFileImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
public DatabaseImporterResult convert() {
|
||||||
List<DatabaseEntry> entries = new ArrayList<>();
|
DatabaseImporterResult result = new DatabaseImporterResult();
|
||||||
|
|
||||||
try {
|
for (XmlEntry xmlEntry : _xmlEntries) {
|
||||||
for (XmlEntry xmlEntry : _xmlEntries) {
|
// TODO: order
|
||||||
if (xmlEntry.Name.equals("tokenOrder")) {
|
if (!xmlEntry.Name.equals("tokenOrder")) {
|
||||||
// TODO: order
|
try {
|
||||||
JSONArray array = new JSONArray(xmlEntry.Value);
|
DatabaseEntry entry = convertEntry(xmlEntry);
|
||||||
} else {
|
result.addEntry(entry);
|
||||||
JSONObject obj = new JSONObject(xmlEntry.Value);
|
} catch (DatabaseImporterEntryException e) {
|
||||||
|
result.addError(e);
|
||||||
String type = obj.getString("type").toLowerCase();
|
|
||||||
String algo = obj.getString("algo");
|
|
||||||
int digits = obj.getInt("digits");
|
|
||||||
byte[] secret = toBytes(obj.getJSONArray("secret"));
|
|
||||||
|
|
||||||
OtpInfo info;
|
|
||||||
if (type.equals("totp")) {
|
|
||||||
info = new TotpInfo(secret, algo, digits, obj.getInt("period"));
|
|
||||||
} else if (type.equals("hotp")) {
|
|
||||||
info = new HotpInfo(secret, algo, digits, obj.getLong("counter"));
|
|
||||||
} else {
|
|
||||||
throw new DatabaseImporterException("unsupported otp type: " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
String issuer = obj.getString("issuerExt");
|
|
||||||
String name = obj.optString("label");
|
|
||||||
|
|
||||||
DatabaseEntry entry = new DatabaseEntry(info, name, issuer);
|
|
||||||
entries.add(entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (OtpInfoException | JSONException e) {
|
|
||||||
throw new DatabaseImporterException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DatabaseEntry convertEntry(XmlEntry xmlEntry) throws DatabaseImporterEntryException {
|
||||||
|
try {
|
||||||
|
JSONObject obj = new JSONObject(xmlEntry.Value);
|
||||||
|
|
||||||
|
String type = obj.getString("type").toLowerCase();
|
||||||
|
String algo = obj.getString("algo");
|
||||||
|
int digits = obj.getInt("digits");
|
||||||
|
byte[] secret = toBytes(obj.getJSONArray("secret"));
|
||||||
|
|
||||||
|
OtpInfo info;
|
||||||
|
if (type.equals("totp")) {
|
||||||
|
info = new TotpInfo(secret, algo, digits, obj.getInt("period"));
|
||||||
|
} else if (type.equals("hotp")) {
|
||||||
|
info = new HotpInfo(secret, algo, digits, obj.getLong("counter"));
|
||||||
|
} else {
|
||||||
|
throw new DatabaseImporterException("unsupported otp type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
String issuer = obj.getString("issuerExt");
|
||||||
|
String name = obj.optString("label");
|
||||||
|
return new DatabaseEntry(info, name, issuer);
|
||||||
|
} catch (DatabaseImporterException | OtpInfoException | JSONException e) {
|
||||||
|
throw new DatabaseImporterEntryException(e, xmlEntry.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class GoogleAuthAppImporter extends DatabaseAppImporter {
|
||||||
@SuppressLint("SdCardPath")
|
@SuppressLint("SdCardPath")
|
||||||
private static final String _filename = "/data/data/com.google.android.apps.authenticator2/databases/databases";
|
private static final String _filename = "/data/data/com.google.android.apps.authenticator2/databases/databases";
|
||||||
|
|
||||||
private List<DatabaseEntry> _entries = new ArrayList<>();
|
private List<Entry> _entries = new ArrayList<>();
|
||||||
|
|
||||||
public GoogleAuthAppImporter(Context context) {
|
public GoogleAuthAppImporter(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -61,34 +61,11 @@ public class GoogleAuthAppImporter extends DatabaseAppImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int type = getInt(cursor, "type");
|
Entry entry = new Entry(cursor);
|
||||||
byte[] secret = Base32.decode(getString(cursor, "secret").toCharArray());
|
|
||||||
|
|
||||||
OtpInfo info;
|
|
||||||
switch (type) {
|
|
||||||
case TYPE_TOTP:
|
|
||||||
info = new TotpInfo(secret);
|
|
||||||
break;
|
|
||||||
case TYPE_HOTP:
|
|
||||||
info = new HotpInfo(secret, getInt(cursor, "counter"));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new DatabaseImporterException("unsupported otp type: " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = getString(cursor, "email", "");
|
|
||||||
String issuer = getString(cursor, "issuer", "");
|
|
||||||
|
|
||||||
String[] parts = name.split(":");
|
|
||||||
if (parts.length == 2) {
|
|
||||||
name = parts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
DatabaseEntry entry = new DatabaseEntry(info, name, issuer);
|
|
||||||
_entries.add(entry);
|
_entries.add(entry);
|
||||||
} while(cursor.moveToNext());
|
} while(cursor.moveToNext());
|
||||||
}
|
}
|
||||||
} catch (SQLiteException | OtpInfoException | Base32Exception e) {
|
} catch (SQLiteException e) {
|
||||||
throw new DatabaseImporterException(e);
|
throw new DatabaseImporterException(e);
|
||||||
} finally {
|
} finally {
|
||||||
// always delete the temporary file
|
// always delete the temporary file
|
||||||
|
@ -97,8 +74,47 @@ public class GoogleAuthAppImporter extends DatabaseAppImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DatabaseEntry> convert() {
|
public DatabaseImporterResult convert() {
|
||||||
return _entries;
|
DatabaseImporterResult result = new DatabaseImporterResult();
|
||||||
|
|
||||||
|
for (Entry sqlEntry : _entries) {
|
||||||
|
try {
|
||||||
|
DatabaseEntry entry = convertEntry(sqlEntry);
|
||||||
|
result.addEntry(entry);
|
||||||
|
} catch (DatabaseImporterEntryException e) {
|
||||||
|
result.addError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DatabaseEntry convertEntry(Entry entry) throws DatabaseImporterEntryException {
|
||||||
|
try {
|
||||||
|
byte[] secret = Base32.decode(entry.getSecret().toCharArray());
|
||||||
|
|
||||||
|
OtpInfo info;
|
||||||
|
switch (entry.getType()) {
|
||||||
|
case TYPE_TOTP:
|
||||||
|
info = new TotpInfo(secret);
|
||||||
|
break;
|
||||||
|
case TYPE_HOTP:
|
||||||
|
info = new HotpInfo(secret, entry.getCounter());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DatabaseImporterException("unsupported otp type: " + entry.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = entry.getEmail();
|
||||||
|
String[] parts = name.split(":");
|
||||||
|
if (parts.length == 2) {
|
||||||
|
name = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DatabaseEntry(info, name, entry.getIssuer());
|
||||||
|
} catch (Base32Exception | OtpInfoException | DatabaseImporterException e) {
|
||||||
|
throw new DatabaseImporterEntryException(e, entry.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,4 +137,44 @@ public class GoogleAuthAppImporter extends DatabaseAppImporter {
|
||||||
private static int getInt(Cursor cursor, String columnName) {
|
private static int getInt(Cursor cursor, String columnName) {
|
||||||
return cursor.getInt(cursor.getColumnIndex(columnName));
|
return cursor.getInt(cursor.getColumnIndex(columnName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long getLong(Cursor cursor, String columnName) {
|
||||||
|
return cursor.getLong(cursor.getColumnIndex(columnName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Entry {
|
||||||
|
private int _type;
|
||||||
|
private String _secret;
|
||||||
|
private String _email;
|
||||||
|
private String _issuer;
|
||||||
|
private long _counter;
|
||||||
|
|
||||||
|
public Entry(Cursor cursor) {
|
||||||
|
_type = getInt(cursor, "type");
|
||||||
|
_secret = getString(cursor, "secret");
|
||||||
|
_email = getString(cursor, "email", "");
|
||||||
|
_issuer = getString(cursor, "issuer", "");
|
||||||
|
_counter = getLong(cursor, "counter");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecret() {
|
||||||
|
return _secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return _email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIssuer() {
|
||||||
|
return _issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCounter() {
|
||||||
|
return _counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ import com.beemdevelopment.aegis.db.DatabaseEntry;
|
||||||
import com.beemdevelopment.aegis.db.DatabaseManager;
|
import com.beemdevelopment.aegis.db.DatabaseManager;
|
||||||
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
|
||||||
|
|
||||||
public class MainActivity extends AegisActivity implements EntryListView.Listener {
|
public class MainActivity extends AegisActivity implements EntryListView.Listener {
|
||||||
// activity request codes
|
// activity request codes
|
||||||
|
|
|
@ -26,9 +26,12 @@ import com.beemdevelopment.aegis.importers.AegisFileImporter;
|
||||||
import com.beemdevelopment.aegis.importers.DatabaseAppImporter;
|
import com.beemdevelopment.aegis.importers.DatabaseAppImporter;
|
||||||
import com.beemdevelopment.aegis.importers.DatabaseFileImporter;
|
import com.beemdevelopment.aegis.importers.DatabaseFileImporter;
|
||||||
import com.beemdevelopment.aegis.importers.DatabaseImporter;
|
import com.beemdevelopment.aegis.importers.DatabaseImporter;
|
||||||
|
import com.beemdevelopment.aegis.importers.DatabaseImporterEntryException;
|
||||||
import com.beemdevelopment.aegis.importers.DatabaseImporterException;
|
import com.beemdevelopment.aegis.importers.DatabaseImporterException;
|
||||||
|
import com.beemdevelopment.aegis.importers.DatabaseImporterResult;
|
||||||
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
|
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
|
||||||
import com.beemdevelopment.aegis.util.ByteInputStream;
|
import com.beemdevelopment.aegis.util.ByteInputStream;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.takisoft.preferencex.PreferenceFragmentCompat;
|
import com.takisoft.preferencex.PreferenceFragmentCompat;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -465,7 +468,10 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importDatabase(DatabaseImporter importer) throws DatabaseImporterException {
|
private void importDatabase(DatabaseImporter importer) throws DatabaseImporterException {
|
||||||
List<DatabaseEntry> entries = importer.convert();
|
DatabaseImporterResult result = importer.convert();
|
||||||
|
List<DatabaseEntry> entries = result.getEntries();
|
||||||
|
List<DatabaseImporterEntryException> errors = result.getErrors();
|
||||||
|
|
||||||
for (DatabaseEntry entry : entries) {
|
for (DatabaseEntry entry : entries) {
|
||||||
// temporary: randomize the UUID of duplicate entries and add them anyway
|
// temporary: randomize the UUID of duplicate entries and add them anyway
|
||||||
if (_db.getEntryByUUID(entry.getUUID()) != null) {
|
if (_db.getEntryByUUID(entry.getUUID()) != null) {
|
||||||
|
@ -480,7 +486,13 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
_result.putExtra("needsRecreate", true);
|
_result.putExtra("needsRecreate", true);
|
||||||
Toast.makeText(getActivity(), String.format(Locale.getDefault(), getString(R.string.imported_entries_count), entries.size()), Toast.LENGTH_LONG).show();
|
Snackbar bar = Snackbar.make(getView(), String.format(Locale.getDefault(), getString(R.string.imported_entries_count), entries.size(), errors.size()), Snackbar.LENGTH_LONG);
|
||||||
|
if (errors.size() == 0) {
|
||||||
|
bar.setAction(R.string.details, v -> {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
bar.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onExport() {
|
private void onExport() {
|
||||||
|
|
|
@ -124,7 +124,7 @@
|
||||||
<string name="file_not_found">Error: File not found</string>
|
<string name="file_not_found">Error: File not found</string>
|
||||||
<string name="reading_file_error">An error occurred while trying to read the file</string>
|
<string name="reading_file_error">An error occurred while trying to read the file</string>
|
||||||
<string name="root_error">Error: unable to obtain root access</string>
|
<string name="root_error">Error: unable to obtain root access</string>
|
||||||
<string name="imported_entries_count">Imported %d entries</string>
|
<string name="imported_entries_count">Imported %d entries. %d errors.</string>
|
||||||
<string name="exporting_database_error">An error occurred while trying to export the database</string>
|
<string name="exporting_database_error">An error occurred while trying to export the database</string>
|
||||||
<string name="export_database_location">The database has been exported to:</string>
|
<string name="export_database_location">The database has been exported to:</string>
|
||||||
<string name="export_warning">This action will export the database out of Aegis\' private storage.</string>
|
<string name="export_warning">This action will export the database out of Aegis\' private storage.</string>
|
||||||
|
@ -140,6 +140,7 @@
|
||||||
<string name="remove_group">Remove group</string>
|
<string name="remove_group">Remove group</string>
|
||||||
<string name="remove_group_description">Are you sure you want to remove this group? Entries in this group will automatically switch to \'No group\'.</string>
|
<string name="remove_group_description">Are you sure you want to remove this group? Entries in this group will automatically switch to \'No group\'.</string>
|
||||||
<string name="adding_new_slot_error">An error occurred while trying to add a new slot:</string>
|
<string name="adding_new_slot_error">An error occurred while trying to add a new slot:</string>
|
||||||
|
<string name="details">Details</string>
|
||||||
<string name="filter">Filter</string>
|
<string name="filter">Filter</string>
|
||||||
<string name="lock">Lock</string>
|
<string name="lock">Lock</string>
|
||||||
<string name="all">All</string>
|
<string name="all">All</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue