When importing, read the file from SAF on a background thread

Fixes issue with importing from Nextcloud. Close #673.
This commit is contained in:
Alexander Bakker 2021-02-07 13:09:51 +01:00
parent 2c8a64f943
commit 293e2ddeb0
4 changed files with 90 additions and 11 deletions

View file

@ -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<? extends DatabaseImporter> importerType = (Class<? extends DatabaseImporter>) getIntent().getSerializableExtra("importerType");
startImport(importerType, getIntent().getStringExtra("fileUri"));
startImport(importerType, (File) getIntent().getSerializableExtra("file"));
}
private void startImport(@NonNull Class<? extends DatabaseImporter> importerType, @Nullable String fileUri) {
if (fileUri == null) {
private void startImport(@NonNull Class<? extends DatabaseImporter> importerType, @Nullable File file) {
if (file == null) {
startImportApp(importerType);
} else {
startImportFile(importerType, Uri.parse(fileUri));
startImportFile(importerType, file);
}
}
private void startImportFile(@NonNull Class<? extends DatabaseImporter> importerType, @NonNull Uri fileUri) {
try (InputStream stream = getContentResolver().openInputStream(fileUri)) {
private void startImportFile(@NonNull Class<? extends DatabaseImporter> importerType, @NonNull File file) {
try (InputStream stream = new FileInputStream(file)) {
DatabaseImporter importer = DatabaseImporter.create(this, importerType);
DatabaseImporter.State state = importer.read(stream);
processImporterState(state);

View file

@ -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<? extends DatabaseImporter> importerType, Uri fileUri) {
private void startImportEntriesActivity(Class<? extends DatabaseImporter> 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);
}

View file

@ -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<Uri, ImportFileTask.Result> {
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;
}
}
}

View file

@ -148,6 +148,7 @@
<string name="snackbar_authentication_method">Please select an authentication method</string>
<string name="encrypting_vault">Encrypting the vault</string>
<string name="exporting_vault">Exporting the vault</string>
<string name="reading_file">Reading file</string>
<string name="delete_entry">Delete entry</string>
<string name="delete_entry_description">Are you sure you want to delete this entry?</string>
<string name="delete_entry_explanation">This action does not disable 2FA for <b>%s</b>. To prevent losing access, make sure that you have disabled 2FA or that you have an alternative way to generate codes for this service.</string>