mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-15 14:32:49 +00:00
Rewrite the export functionality to use the Storage Access Framework
We noticed after the v1.1 release that we need to switch our external storage access code over to use the Storage Access Framework. The export functionality was the only piece of code that still used the deprecated method and this patch addresses that. The Storage Access Framework is a little annoying to work with, but I don't think it'll be too painful for our usecase. This switch comes with some nice benefits for users as well. Users are now able to select a custom storage location, which can be local or in the cloud. This also removes ``requestLegacyExternalStorage`` from the manifest, as we don't need it anymore.
This commit is contained in:
parent
10c206270a
commit
1ede56e137
4 changed files with 46 additions and 43 deletions
|
@ -5,7 +5,6 @@ import android.app.Activity;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Window;
|
||||
|
@ -26,16 +25,6 @@ import com.beemdevelopment.aegis.Theme;
|
|||
import com.beemdevelopment.aegis.ViewMode;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandle;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandleException;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileException;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManagerException;
|
||||
import com.beemdevelopment.aegis.vault.slots.BiometricSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.Slot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotList;
|
||||
import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer;
|
||||
import com.beemdevelopment.aegis.helpers.BiometricsHelper;
|
||||
import com.beemdevelopment.aegis.helpers.PermissionHelper;
|
||||
|
@ -47,6 +36,16 @@ import com.beemdevelopment.aegis.services.NotificationService;
|
|||
import com.beemdevelopment.aegis.ui.models.ImportEntry;
|
||||
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
|
||||
import com.beemdevelopment.aegis.util.UUIDMap;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileCredentials;
|
||||
import com.beemdevelopment.aegis.vault.VaultFileException;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManagerException;
|
||||
import com.beemdevelopment.aegis.vault.slots.BiometricSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
|
||||
import com.beemdevelopment.aegis.vault.slots.Slot;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
||||
import com.beemdevelopment.aegis.vault.slots.SlotList;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||
|
@ -54,6 +53,7 @@ import com.topjohnwu.superuser.io.SuFileInputStream;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -70,6 +70,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
private static final int CODE_SLOTS = 2;
|
||||
private static final int CODE_GROUPS = 3;
|
||||
private static final int CODE_SELECT_ENTRIES = 4;
|
||||
private static final int CODE_EXPORT = 5;
|
||||
private static final int CODE_EXPORT_ENCRYPT = 6;
|
||||
|
||||
// permission request codes
|
||||
private static final int CODE_PERM_IMPORT = 0;
|
||||
|
@ -406,6 +408,11 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
case CODE_SELECT_ENTRIES:
|
||||
onSelectEntriesResult(resultCode, data);
|
||||
break;
|
||||
case CODE_EXPORT:
|
||||
// intentional fallthrough
|
||||
case CODE_EXPORT_ENCRYPT:
|
||||
onExportResult(resultCode, data, requestCode == CODE_EXPORT_ENCRYPT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,18 +594,12 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.pref_export_summary)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
String filename;
|
||||
try {
|
||||
filename = _vault.export(checked.get());
|
||||
} catch (VaultManagerException e) {
|
||||
Toast.makeText(getActivity(), R.string.exporting_vault_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.setType("application/json")
|
||||
.putExtra(Intent.EXTRA_TITLE, checked.get() ? VaultManager.FILENAME_EXPORT : VaultManager.FILENAME_EXPORT_PLAIN);
|
||||
|
||||
// make sure the new file is visible
|
||||
MediaScannerConnection.scanFile(getActivity(), new String[]{filename}, null, null);
|
||||
|
||||
Toast.makeText(getActivity(), getString(R.string.export_vault_location) + filename, Toast.LENGTH_SHORT).show();
|
||||
startActivityForResult(intent, checked.get() ? CODE_EXPORT_ENCRYPT : CODE_EXPORT);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
if (_vault.isEncryptionEnabled()) {
|
||||
|
@ -669,6 +670,22 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
_result.putExtra("needsRecreate", true);
|
||||
}
|
||||
|
||||
private void onExportResult(int resultCode, Intent data, boolean encrypt) {
|
||||
Uri uri = data.getData();
|
||||
if (resultCode != Activity.RESULT_OK || uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (OutputStream stream = getContext().getContentResolver().openOutputStream(uri, "w")) {
|
||||
_vault.export(stream, encrypt);
|
||||
} catch (IOException | VaultManagerException e) {
|
||||
Toast.makeText(getActivity(), R.string.exporting_vault_error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(getActivity(), getString(R.string.exported_vault), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private boolean saveVault() {
|
||||
try {
|
||||
_vault.save();
|
||||
|
|
|
@ -2,10 +2,7 @@ package com.beemdevelopment.aegis.vault;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.beemdevelopment.aegis.BuildConfig;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.services.NotificationService;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
@ -15,14 +12,15 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.Collator;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class VaultManager {
|
||||
private static final String FILENAME = "aegis.json";
|
||||
private static final String FILENAME_EXPORT = "aegis_export.json";
|
||||
private static final String FILENAME_EXPORT_PLAIN = "aegis_export_plain.json";
|
||||
public static final String FILENAME_EXPORT = "aegis_export";
|
||||
public static final String FILENAME_EXPORT_PLAIN = "aegis_export_plain";
|
||||
|
||||
private Vault _vault;
|
||||
private VaultFile _file;
|
||||
|
@ -104,7 +102,7 @@ public class VaultManager {
|
|||
}
|
||||
}
|
||||
|
||||
public String export(boolean encrypt) throws VaultManagerException {
|
||||
public void export(OutputStream stream, boolean encrypt) throws VaultManagerException {
|
||||
assertState(false, true);
|
||||
|
||||
try {
|
||||
|
@ -115,19 +113,8 @@ public class VaultManager {
|
|||
vaultFile.setContent(_vault.toJson());
|
||||
}
|
||||
|
||||
String dirName = !BuildConfig.DEBUG ? _context.getString(R.string.app_name) : _context.getString(R.string.app_name_dev);
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), dirName);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("error creating external storage directory");
|
||||
}
|
||||
|
||||
byte[] bytes = vaultFile.toBytes();
|
||||
File file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN);
|
||||
try (FileOutputStream stream = new FileOutputStream(file)) {
|
||||
stream.write(bytes);
|
||||
}
|
||||
|
||||
return file.getAbsolutePath();
|
||||
stream.write(bytes);
|
||||
} catch (IOException | VaultFileException e) {
|
||||
throw new VaultManagerException(e);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue