diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java
index 7f2638e3..daf8d416 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java
@@ -13,6 +13,7 @@ import com.topjohnwu.superuser.io.SuFileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
@@ -59,6 +60,10 @@ public abstract class DatabaseImporter {
return new SuFile(man.getApplicationInfo(pkgName, 0).dataDir, subPath);
}
+ public boolean isInstalledAppVersionSupported() {
+ return true;
+ }
+
protected abstract State read(InputStream stream, boolean isInternal) throws DatabaseImporterException;
public State read(InputStream stream) throws DatabaseImporterException {
@@ -91,7 +96,7 @@ public abstract class DatabaseImporter {
return Collections.unmodifiableList(_importers);
}
- public static class Definition {
+ public static class Definition implements Serializable {
private final String _name;
private final Class extends DatabaseImporter> _type;
private final @StringRes int _help;
diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java
index 58cbc040..4070bcbb 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/importers/GoogleAuthImporter.java
@@ -1,6 +1,7 @@
package com.beemdevelopment.aegis.importers;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
@@ -32,7 +33,19 @@ public class GoogleAuthImporter extends DatabaseImporter {
protected SuFile getAppPath() throws PackageManager.NameNotFoundException {
return getAppPath(_pkgName, _subPath);
}
-
+
+ @Override
+ public boolean isInstalledAppVersionSupported() {
+ PackageInfo info;
+ try {
+ info = requireContext().getPackageManager().getPackageInfo(_pkgName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ return info.versionCode <= 5000100;
+ }
+
@Override
public State read(InputStream stream, boolean isInternal) throws DatabaseImporterException {
final Context context = requireContext();
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java
index 02399c76..f8a1e89e 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java
@@ -83,21 +83,35 @@ public class ImportEntriesActivity extends AegisActivity {
});
_fabScrollHelper = new FabScrollHelper(fab);
- Class extends DatabaseImporter> importerType = (Class extends DatabaseImporter>) getIntent().getSerializableExtra("importerType");
- startImport(importerType, (File) getIntent().getSerializableExtra("file"));
+ DatabaseImporter.Definition importerDef = (DatabaseImporter.Definition) getIntent().getSerializableExtra("importerDef");
+ startImport(importerDef, (File) getIntent().getSerializableExtra("file"));
}
- private void startImport(@NonNull Class extends DatabaseImporter> importerType, @Nullable File file) {
+ private void startImport(DatabaseImporter.Definition importerDef, @Nullable File file) {
+ DatabaseImporter importer = DatabaseImporter.create(this, importerDef.getType());
if (file == null) {
- startImportApp(importerType);
+ if (importer.isInstalledAppVersionSupported()) {
+ startImportApp(importer);
+ } else {
+ Dialogs.showSecureDialog(new AlertDialog.Builder(this)
+ .setTitle(R.string.warning)
+ .setMessage(getString(R.string.app_version_error, importerDef.getName()))
+ .setCancelable(false)
+ .setPositiveButton(R.string.yes, (dialog1, which) -> {
+ startImportApp(importer);
+ })
+ .setNegativeButton(R.string.no, (dialog1, which) -> {
+ finish();
+ })
+ .create());
+ }
} else {
- startImportFile(importerType, file);
+ startImportFile(importer, file);
}
}
- private void startImportFile(@NonNull Class extends DatabaseImporter> importerType, @NonNull File file) {
+ private void startImportFile(@NonNull DatabaseImporter importer, @NonNull File file) {
try (InputStream stream = new FileInputStream(file)) {
- DatabaseImporter importer = DatabaseImporter.create(this, importerType);
DatabaseImporter.State state = importer.read(stream);
processImporterState(state);
} catch (FileNotFoundException e) {
@@ -108,9 +122,7 @@ public class ImportEntriesActivity extends AegisActivity {
}
}
- private void startImportApp(@NonNull Class extends DatabaseImporter> importerType) {
- DatabaseImporter importer = DatabaseImporter.create(this, importerType);
-
+ private void startImportApp(@NonNull DatabaseImporter importer) {
// obtain the global root shell and close it immediately after we're done
// TODO: find a way to use SuFileInputStream with Shell.newInstance()
try (Shell shell = Shell.getShell()) {
diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java
index 3f592bf2..49877350 100644
--- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java
+++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java
@@ -42,7 +42,7 @@ import javax.crypto.Cipher;
public class ImportExportPreferencesFragment extends PreferencesFragment {
// keep a reference to the type of database converter that was selected
- private Class extends DatabaseImporter> _importerType;
+ private DatabaseImporter.Definition _importerDef;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -50,13 +50,13 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
addPreferencesFromResource(R.xml.preferences_import_export);
if (savedInstanceState != null) {
- _importerType = (Class extends DatabaseImporter>) savedInstanceState.getSerializable("importerType");
+ _importerDef = (DatabaseImporter.Definition) savedInstanceState.getSerializable("importerDef");
}
Preference importPreference = requirePreference("pref_import");
importPreference.setOnPreferenceClickListener(preference -> {
Dialogs.showImportersDialog(requireContext(), false, definition -> {
- _importerType = definition.getType();
+ _importerDef = definition;
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
@@ -68,7 +68,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
Preference importAppPreference = requirePreference("pref_import_app");
importAppPreference.setOnPreferenceClickListener(preference -> {
Dialogs.showImportersDialog(requireContext(), true, definition -> {
- startImportEntriesActivity(definition.getType(), null);
+ startImportEntriesActivity(definition, null);
});
return true;
});
@@ -83,7 +83,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putSerializable("importerType", _importerType);
+ outState.putSerializable("importerDef", _importerDef);
}
@Override
@@ -115,7 +115,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
ImportFileTask.Params params = new ImportFileTask.Params(uri, "import", null);
ImportFileTask task = new ImportFileTask(requireContext(), result -> {
if (result.getException() == null) {
- startImportEntriesActivity(_importerType, result.getFile());
+ startImportEntriesActivity(_importerDef, result.getFile());
} else {
Dialogs.showErrorDialog(requireContext(), R.string.reading_file_error, result.getException());
}
@@ -123,9 +123,9 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
task.execute(getLifecycle(), params);
}
- private void startImportEntriesActivity(Class extends DatabaseImporter> importerType, File file) {
+ private void startImportEntriesActivity(DatabaseImporter.Definition importerDef, File file) {
Intent intent = new Intent(requireActivity(), ImportEntriesActivity.class);
- intent.putExtra("importerType", importerType);
+ intent.putExtra("importerDef", importerDef);
intent.putExtra("file", file);
startActivityForResult(intent, CODE_IMPORT);
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 946254cb..b14ffee7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,6 +16,7 @@
PIN (4–16 digits)
Suggested
Usage count
+ Warning
App
Entries
@@ -205,6 +206,7 @@
Error: File not found
An error occurred while trying to read the file
Error: App is not installed
+ The version of %s that\'s installed is not supported. Attempting to import may result in an error. Would you like to continue anyway?
Error: unable to obtain root access
- Imported %d entry
@@ -381,14 +383,14 @@
Supply a copy of /data/data/com.duosecurity.duomobile/files/duokit/accounts.json, located in the internal storage directory of DUO.
Supply a copy of /data/data/org.fedorahosted.freeotp/shared_prefs/tokens.xml, located in the internal storage directory of FreeOTP.
Supply a FreeOTP+ export file.
- Supply a copy of /data/data/com.google.android.apps.authenticator2/databases/databases, located in the internal storage directory of Google Authenticator.
+ Only database files from Google Authenticator v5.10 and prior are supported.\n\nSupply a copy of /data/data/com.google.android.apps.authenticator2/databases/databases, located in the internal storage directory of Google Authenticator.
Supply a copy of /data/data/com.azure.authenticator/databases/PhoneFactor, located in the internal storage directory of Microsoft Authenticator.
Supply a plain text file with a Google Authenticator URI on each line.
Supply a copy of /data/data/com.valvesoftware.android.steam.community/files/Steamguard-*.json, located in the internal storage directory of Steam.
Supply a TOTP Authenticator export file.
Supply a WinAuth export file.
- "Encrypted entry was skipped: %s"
+ Encrypted entry was skipped: %s
Import entries directly from %s. This requires the app to be installed on this device and for root access to be granted to Aegis.
Groups