From 293e2ddeb047fe7286ed194a4000ab98c18f2ac6 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sun, 7 Feb 2021 13:09:51 +0100 Subject: [PATCH] When importing, read the file from SAF on a background thread Fixes issue with importing from Nextcloud. Close #673. --- .../aegis/ui/ImportEntriesActivity.java | 15 ++-- .../ImportExportPreferencesFragment.java | 16 +++-- .../aegis/ui/tasks/ImportFileTask.java | 69 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ImportFileTask.java 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 47e4821b..d3171839 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java @@ -4,7 +4,6 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.Menu; @@ -32,6 +31,8 @@ import com.beemdevelopment.aegis.vault.VaultManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.topjohnwu.superuser.Shell; +import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -78,19 +79,19 @@ public class ImportEntriesActivity extends AegisActivity { _fabScrollHelper = new FabScrollHelper(fab); Class importerType = (Class) getIntent().getSerializableExtra("importerType"); - startImport(importerType, getIntent().getStringExtra("fileUri")); + startImport(importerType, (File) getIntent().getSerializableExtra("file")); } - private void startImport(@NonNull Class importerType, @Nullable String fileUri) { - if (fileUri == null) { + private void startImport(@NonNull Class importerType, @Nullable File file) { + if (file == null) { startImportApp(importerType); } else { - startImportFile(importerType, Uri.parse(fileUri)); + startImportFile(importerType, file); } } - private void startImportFile(@NonNull Class importerType, @NonNull Uri fileUri) { - try (InputStream stream = getContentResolver().openInputStream(fileUri)) { + private void startImportFile(@NonNull Class importerType, @NonNull File file) { + try (InputStream stream = new FileInputStream(file)) { DatabaseImporter importer = DatabaseImporter.create(this, importerType); DatabaseImporter.State state = importer.read(stream); processImporterState(state); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java index d17fd990..1019fa3f 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/ImportExportPreferencesFragment.java @@ -22,9 +22,10 @@ import com.beemdevelopment.aegis.BuildConfig; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.helpers.DropdownHelper; import com.beemdevelopment.aegis.importers.DatabaseImporter; -import com.beemdevelopment.aegis.ui.dialogs.Dialogs; import com.beemdevelopment.aegis.ui.ImportEntriesActivity; +import com.beemdevelopment.aegis.ui.dialogs.Dialogs; import com.beemdevelopment.aegis.ui.tasks.ExportTask; +import com.beemdevelopment.aegis.ui.tasks.ImportFileTask; import com.beemdevelopment.aegis.vault.VaultBackupManager; import com.beemdevelopment.aegis.vault.VaultFileCredentials; import com.beemdevelopment.aegis.vault.VaultManager; @@ -111,13 +112,20 @@ public class ImportExportPreferencesFragment extends PreferencesFragment { return; } - startImportEntriesActivity(_importerType, uri); + ImportFileTask task = new ImportFileTask(getContext(), result -> { + if (result.getException() == null) { + startImportEntriesActivity(_importerType, result.getFile()); + } else { + Dialogs.showErrorDialog(getContext(), R.string.reading_file_error, result.getException()); + } + }); + task.execute(getLifecycle(), uri); } - private void startImportEntriesActivity(Class importerType, Uri fileUri) { + private void startImportEntriesActivity(Class importerType, File file) { Intent intent = new Intent(getActivity(), ImportEntriesActivity.class); intent.putExtra("importerType", importerType); - intent.putExtra("fileUri", fileUri == null ? null : fileUri.toString()); + intent.putExtra("file", file); startActivityForResult(intent, CODE_IMPORT); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ImportFileTask.java b/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ImportFileTask.java new file mode 100644 index 00000000..4bf97df9 --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ImportFileTask.java @@ -0,0 +1,69 @@ +package com.beemdevelopment.aegis.ui.tasks; + +import android.content.Context; +import android.net.Uri; + +import com.beemdevelopment.aegis.R; +import com.beemdevelopment.aegis.util.IOUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * ImportFileTask reads an SAF file from a background thread and + * writes it to a temporary file in the cache directory. + */ +public class ImportFileTask extends ProgressDialogTask { + private final Callback _cb; + + public ImportFileTask(Context context, Callback cb) { + super(context, context.getString(R.string.reading_file)); + _cb = cb; + } + + @Override + protected Result doInBackground(Uri... uris) { + Context context = getDialog().getContext(); + + try (InputStream inStream = context.getContentResolver().openInputStream(uris[0])) { + File tempFile = File.createTempFile("import-", "", context.getCacheDir()); + try (FileOutputStream outStream = new FileOutputStream(tempFile)) { + IOUtils.copy(inStream, outStream); + } + + return new Result(tempFile, null); + } catch (IOException e) { + return new Result(null, e); + } + } + + @Override + protected void onPostExecute(Result result) { + super.onPostExecute(result); + _cb.onTaskFinished(result); + } + + public interface Callback { + void onTaskFinished(Result result); + } + + public static class Result { + private final File _file; + private final Exception _e; + + public Result(File file, Exception e) { + _file = file; + _e = e; + } + + public File getFile() { + return _file; + } + + public Exception getException() { + return _e; + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c0a10d9b..f80ac5c9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,6 +148,7 @@ Please select an authentication method Encrypting the vault Exporting the vault + Reading file Delete entry Are you sure you want to delete this entry? This action does not disable 2FA for %s. To prevent losing access, make sure that you have disabled 2FA or that you have an alternative way to generate codes for this service.