mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-21 22:39:12 +00:00
Add basic support for exporting the database
This commit is contained in:
parent
71eb487f85
commit
95638b359b
7 changed files with 163 additions and 46 deletions
|
@ -5,6 +5,7 @@
|
|||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
|
@ -171,7 +171,7 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
|
|||
_databaseFile.setContent(result.Data);
|
||||
_databaseFile.setCryptParameters(result.Parameters);
|
||||
}
|
||||
_databaseFile.save(getApplicationContext(), DatabaseManager.FILENAME);
|
||||
DatabaseManager.save(getApplicationContext(), _databaseFile);
|
||||
} catch (Exception e) {
|
||||
setException(e);
|
||||
return;
|
||||
|
|
|
@ -3,11 +3,13 @@ package me.impy.aegis;
|
|||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.design.widget.BottomSheetDialog;
|
||||
|
@ -46,6 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
private static final int CODE_DO_INTRO = 2;
|
||||
private static final int CODE_DECRYPT = 3;
|
||||
private static final int CODE_IMPORT = 4;
|
||||
private static final int CODE_PREFERENCES = 5;
|
||||
|
||||
private KeyProfileAdapter _keyProfileAdapter;
|
||||
private ArrayList<KeyProfile> _keyProfiles;
|
||||
|
@ -140,6 +143,54 @@ public class MainActivity extends AppCompatActivity {
|
|||
case CODE_IMPORT:
|
||||
onImportResult(resultCode, data);
|
||||
break;
|
||||
case CODE_PREFERENCES:
|
||||
onPreferencesResult(resultCode, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onPreferencesResult(int resultCode, Intent data) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: create a custom layout to show a message AND a checkbox
|
||||
int action = data.getIntExtra("action", -1);
|
||||
switch (action) {
|
||||
case PreferencesActivity.ACTION_EXPORT:
|
||||
final boolean[] checked = {true};
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
|
||||
.setTitle("Export the database")
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
String filename;
|
||||
try {
|
||||
filename = _db.export(checked[0]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, "An error occurred while trying to export the database", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure the new file is visible
|
||||
MediaScannerConnection.scanFile(this, new String[]{filename}, null, null);
|
||||
|
||||
Toast.makeText(this, "The database has been exported to: " + filename, Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
if (_db.getFile().isEncrypted()) {
|
||||
final String[] items = {"Keep the database encrypted"};
|
||||
final boolean[] checkedItems = {true};
|
||||
builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int index, boolean isChecked) {
|
||||
checked[0] = isChecked;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
builder.setMessage("This action will export the database out of Android's private storage.");
|
||||
}
|
||||
builder.show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,7 +422,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
switch (item.getItemId()) {
|
||||
case R.id.action_settings:
|
||||
Intent preferencesActivity = new Intent(this, PreferencesActivity.class);
|
||||
startActivity(preferencesActivity);
|
||||
startActivityForResult(preferencesActivity, CODE_PREFERENCES);
|
||||
return true;
|
||||
case R.id.action_import:
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package me.impy.aegis;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
|
@ -9,13 +11,14 @@ import android.support.v7.app.AppCompatActivity;
|
|||
import android.widget.Toast;
|
||||
|
||||
public class PreferencesActivity extends AppCompatActivity {
|
||||
public static final int ACTION_EXPORT = 0;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
SharedPreferences mySharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (mySharedPreferences.getBoolean("pref_night_mode", false)) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (preferences.getBoolean("pref_night_mode", false)) {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
} else {
|
||||
setTheme(R.style.AppTheme_Default);
|
||||
|
@ -29,7 +32,6 @@ public class PreferencesActivity extends AppCompatActivity {
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
final Preference nightModePreference = findPreference("pref_night_mode");
|
||||
|
@ -40,6 +42,20 @@ public class PreferencesActivity extends AppCompatActivity {
|
|||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Preference exportPreference = findPreference("pref_export");
|
||||
exportPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("action", ACTION_EXPORT);
|
||||
|
||||
Activity activity = getActivity();
|
||||
activity.setResult(RESULT_OK, intent);
|
||||
activity.finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -116,37 +117,6 @@ public class DatabaseFile {
|
|||
return !_slots.isEmpty() && _cryptParameters != null;
|
||||
}
|
||||
|
||||
public void save(Context context, String filename) throws IOException {
|
||||
byte[] data = serialize();
|
||||
|
||||
FileOutputStream file = context.openFileOutput(filename, Context.MODE_PRIVATE);
|
||||
file.write(data);
|
||||
file.close();
|
||||
}
|
||||
|
||||
public static DatabaseFile load(Context context, String filename) throws Exception {
|
||||
byte[] bytes;
|
||||
FileInputStream file = null;
|
||||
|
||||
try {
|
||||
file = context.openFileInput(filename);
|
||||
DataInputStream stream = new DataInputStream(file);
|
||||
bytes = new byte[(int) file.getChannel().size()];
|
||||
stream.readFully(bytes);
|
||||
stream.close();
|
||||
} finally {
|
||||
// always close the file
|
||||
// there is no need to close the DataInputStream
|
||||
if (file != null) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseFile db = new DatabaseFile();
|
||||
db.deserialize(bytes);
|
||||
return db;
|
||||
}
|
||||
|
||||
private static void writeSection(DataOutputStream stream, byte id, byte[] data) throws IOException {
|
||||
stream.write(id);
|
||||
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
package me.impy.aegis.db;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import me.impy.aegis.KeyProfile;
|
||||
import me.impy.aegis.crypto.CryptParameters;
|
||||
import me.impy.aegis.crypto.CryptResult;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
|
||||
public class DatabaseManager {
|
||||
public static final String FILENAME = "aegis.db";
|
||||
private static final String FILENAME = "aegis.db";
|
||||
private static final String FILENAME_EXPORT = "aegis_export.db";
|
||||
private static final String FILENAME_EXPORT_PLAIN = "aegis_export.json";
|
||||
|
||||
private MasterKey _key;
|
||||
private DatabaseFile _file;
|
||||
|
@ -22,11 +29,30 @@ public class DatabaseManager {
|
|||
}
|
||||
|
||||
public void load() throws Exception {
|
||||
_file = DatabaseFile.load(_context, FILENAME);
|
||||
byte[] fileBytes;
|
||||
FileInputStream file = null;
|
||||
|
||||
try {
|
||||
file = _context.openFileInput(FILENAME);
|
||||
fileBytes = new byte[(int) file.getChannel().size()];
|
||||
DataInputStream stream = new DataInputStream(file);
|
||||
stream.readFully(fileBytes);
|
||||
stream.close();
|
||||
} finally {
|
||||
// always close the file stream
|
||||
// there is no need to close the DataInputStream
|
||||
if (file != null) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
_file = new DatabaseFile();
|
||||
_file.deserialize(fileBytes);
|
||||
|
||||
if (!_file.isEncrypted()) {
|
||||
byte[] bytes = _file.getContent();
|
||||
byte[] contentBytes = _file.getContent();
|
||||
_db = new Database();
|
||||
_db.deserialize(bytes);
|
||||
_db.deserialize(contentBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,17 +66,64 @@ public class DatabaseManager {
|
|||
_key = key;
|
||||
}
|
||||
|
||||
public static void save(Context context, DatabaseFile file) throws IOException {
|
||||
byte[] bytes = file.serialize();
|
||||
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
stream = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
|
||||
stream.write(bytes);
|
||||
} finally {
|
||||
// always close the file stream
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() throws Exception {
|
||||
assertDecrypted();
|
||||
byte[] bytes = _db.serialize();
|
||||
byte[] dbBytes = _db.serialize();
|
||||
if (!_file.isEncrypted()) {
|
||||
_file.setContent(bytes);
|
||||
_file.setContent(dbBytes);
|
||||
} else {
|
||||
CryptResult result = _key.encrypt(bytes);
|
||||
CryptResult result = _key.encrypt(dbBytes);
|
||||
_file.setContent(result.Data);
|
||||
_file.setCryptParameters(result.Parameters);
|
||||
}
|
||||
_file.save(_context, FILENAME);
|
||||
save(_context, _file);
|
||||
}
|
||||
|
||||
public String export(boolean encrypt) throws Exception {
|
||||
assertDecrypted();
|
||||
byte[] bytes = _db.serialize();
|
||||
encrypt = encrypt && getFile().isEncrypted();
|
||||
if (encrypt) {
|
||||
CryptResult result = _key.encrypt(bytes);
|
||||
_file.setContent(result.Data);
|
||||
_file.setCryptParameters(result.Parameters);
|
||||
bytes = _file.serialize();
|
||||
}
|
||||
|
||||
File file;
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), "Aegis");
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("error creating external storage directory");
|
||||
}
|
||||
|
||||
file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN);
|
||||
stream = new FileOutputStream(file);
|
||||
stream.write(bytes);
|
||||
} finally {
|
||||
// always close the file stream
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public void addKey(DatabaseEntry entry) throws Exception {
|
||||
|
|
|
@ -13,4 +13,10 @@
|
|||
android:key="pref_issuer"
|
||||
android:title="@string/pref_issuers"
|
||||
android:summary="@string/pref_issuers_description"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_export"
|
||||
android:title="Export"
|
||||
android:summary="Export the database"/>
|
||||
|
||||
</PreferenceScreen>
|
Loading…
Add table
Reference in a new issue