Refactor backup activity to better work with background tasks
This commit is contained in:
parent
e1ced76b55
commit
feeeba7294
7 changed files with 417 additions and 408 deletions
|
@ -36,11 +36,16 @@ import android.text.TextUtils;
|
|||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Switch;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
|
@ -48,6 +53,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
|||
import org.shadowice.flocke.andotp.Database.Entry;
|
||||
import org.shadowice.flocke.andotp.Dialogs.PasswordEntryDialog;
|
||||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Tasks.BackupTask;
|
||||
import org.shadowice.flocke.andotp.Utilities.BackupHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
|
||||
|
@ -67,9 +73,11 @@ import java.util.concurrent.Executors;
|
|||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
public class BackupActivity extends BaseActivity {
|
||||
public class BackupActivity extends BaseActivity
|
||||
implements BackupTask.BackupCallback {
|
||||
private final static String TAG = BackupActivity.class.getSimpleName();
|
||||
|
||||
private Constants.BackupType backupType = Constants.BackupType.ENCRYPTED;
|
||||
private SecretKey encryptionKey = null;
|
||||
|
||||
private OpenPgpServiceConnection pgpServiceConnection;
|
||||
|
@ -78,7 +86,14 @@ public class BackupActivity extends BaseActivity {
|
|||
private Uri encryptTargetFile;
|
||||
private Uri decryptSourceFile;
|
||||
|
||||
private Switch replace;
|
||||
private Button btnBackup;
|
||||
private Button btnRestore;
|
||||
private TextView txtBackupLabel;
|
||||
private TextView txtBackupWarning;
|
||||
private SwitchMaterial swReplace;
|
||||
private CheckBox chkOldFormat;
|
||||
private ProgressBar progressBackup;
|
||||
private ProgressBar progressRestore;
|
||||
|
||||
private boolean reload = false;
|
||||
|
||||
|
@ -100,96 +115,119 @@ public class BackupActivity extends BaseActivity {
|
|||
byte[] keyMaterial = callingIntent.getByteArrayExtra(Constants.EXTRA_BACKUP_ENCRYPTION_KEY);
|
||||
encryptionKey = EncryptionHelper.generateSymmetricKey(keyMaterial);
|
||||
|
||||
// Plain-text
|
||||
Spinner spBackupType = v.findViewById(R.id.backupType);
|
||||
btnBackup = v.findViewById(R.id.buttonBackup);
|
||||
btnRestore = v.findViewById(R.id.buttonRestore);
|
||||
txtBackupLabel = v.findViewById(R.id.backupLabel);
|
||||
txtBackupWarning = v.findViewById(R.id.backupErrorLabel);
|
||||
swReplace = v.findViewById(R.id.backup_replace);
|
||||
chkOldFormat = v.findViewById(R.id.restoreOldCrypt);
|
||||
progressBackup = v.findViewById(R.id.progressBarBackup);
|
||||
progressRestore = v.findViewById(R.id.progressBarRestore);
|
||||
|
||||
LinearLayout backupPlain = v.findViewById(R.id.button_backup_plain);
|
||||
LinearLayout restorePlain = v.findViewById(R.id.button_restore_plain);
|
||||
setupBackupType(settings.getDefaultBackupType());
|
||||
spBackupType.setSelection(backupType.ordinal());
|
||||
|
||||
backupPlain.setOnClickListener(new View.OnClickListener() {
|
||||
spBackupType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
Constants.BackupType type = Constants.BackupType.values()[i];
|
||||
setupBackupType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) { }
|
||||
});
|
||||
|
||||
btnBackup.setOnClickListener(view -> {
|
||||
switch (backupType) {
|
||||
case PLAIN_TEXT:
|
||||
backupPlainWithWarning();
|
||||
}
|
||||
});
|
||||
|
||||
restorePlain.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN);
|
||||
}
|
||||
});
|
||||
|
||||
// Password
|
||||
|
||||
TextView cryptSetup = v.findViewById(R.id.msg_crypt_setup);
|
||||
LinearLayout backupCrypt = v.findViewById(R.id.button_backup_crypt);
|
||||
LinearLayout restoreCrypt = v.findViewById(R.id.button_restore_crypt);
|
||||
LinearLayout restoreCryptOld = v.findViewById(R.id.button_restore_crypt_old);
|
||||
|
||||
if (settings.getBackupPasswordEnc().isEmpty()) {
|
||||
cryptSetup.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
cryptSetup.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
backupCrypt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
break;
|
||||
case ENCRYPTED:
|
||||
showSaveFileSelector(Constants.BACKUP_MIMETYPE_CRYPT, Constants.BackupType.ENCRYPTED, Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT);
|
||||
break;
|
||||
case OPEN_PGP:
|
||||
showSaveFileSelector(Constants.BACKUP_MIMETYPE_PGP, Constants.BackupType.OPEN_PGP, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
restoreCrypt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT);
|
||||
}
|
||||
});
|
||||
|
||||
restoreCryptOld.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
btnRestore.setOnClickListener(view -> {
|
||||
switch (backupType) {
|
||||
case PLAIN_TEXT:
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN);
|
||||
break;
|
||||
case ENCRYPTED:
|
||||
if (chkOldFormat.isChecked())
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD);
|
||||
else
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT);
|
||||
break;
|
||||
case OPEN_PGP:
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// OpenPGP
|
||||
private void setupBackupType(Constants.BackupType type) {
|
||||
switch (type) {
|
||||
case PLAIN_TEXT:
|
||||
txtBackupLabel.setText(R.string.backup_label_warning_plain);
|
||||
|
||||
chkOldFormat.setVisibility(View.GONE);
|
||||
txtBackupWarning.setVisibility(View.GONE);
|
||||
|
||||
btnBackup.setEnabled(true);
|
||||
btnRestore.setEnabled(true);
|
||||
|
||||
break;
|
||||
case ENCRYPTED:
|
||||
txtBackupLabel.setText(R.string.backup_label_crypt);
|
||||
|
||||
chkOldFormat.setVisibility(View.VISIBLE);
|
||||
txtBackupWarning.setVisibility(View.GONE);
|
||||
|
||||
btnBackup.setEnabled(true);
|
||||
btnRestore.setEnabled(true);
|
||||
|
||||
break;
|
||||
case OPEN_PGP:
|
||||
txtBackupLabel.setText(R.string.backup_label_pgp);
|
||||
|
||||
chkOldFormat.setVisibility(View.GONE);
|
||||
|
||||
String PGPProvider = settings.getOpenPGPProvider();
|
||||
pgpEncryptionUserIDs = settings.getOpenPGPEncryptionUserIDs();
|
||||
|
||||
TextView setupPGP = v.findViewById(R.id.msg_openpgp_setup);
|
||||
LinearLayout backupPGP = v.findViewById(R.id.button_backup_openpgp);
|
||||
LinearLayout restorePGP = v.findViewById(R.id.button_restore_openpgp);
|
||||
|
||||
if (TextUtils.isEmpty(PGPProvider)) {
|
||||
setupPGP.setVisibility(View.VISIBLE);
|
||||
backupPGP.setVisibility(View.GONE);
|
||||
restorePGP.setVisibility(View.GONE);
|
||||
txtBackupWarning.setText(R.string.backup_desc_openpgp_provider);
|
||||
txtBackupWarning.setVisibility(View.VISIBLE);
|
||||
|
||||
btnBackup.setEnabled(false);
|
||||
btnRestore.setEnabled(false);
|
||||
} else if (TextUtils.isEmpty(pgpEncryptionUserIDs)){
|
||||
setupPGP.setVisibility(View.VISIBLE);
|
||||
setupPGP.setText(R.string.backup_desc_openpgp_keyid);
|
||||
backupPGP.setVisibility(View.GONE);
|
||||
txtBackupWarning.setText(R.string.backup_desc_openpgp_keyid);
|
||||
txtBackupWarning.setVisibility(View.VISIBLE);
|
||||
|
||||
btnBackup.setEnabled(false);
|
||||
btnRestore.setEnabled(false);
|
||||
} else {
|
||||
txtBackupWarning.setVisibility(View.GONE);
|
||||
|
||||
btnBackup.setEnabled(true);
|
||||
btnRestore.setEnabled(true);
|
||||
|
||||
pgpServiceConnection = new OpenPgpServiceConnection(BackupActivity.this.getApplicationContext(), PGPProvider);
|
||||
pgpServiceConnection.bindToService();
|
||||
|
||||
backupPGP.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showSaveFileSelector(Constants.BACKUP_MIMETYPE_PGP, Constants.BackupType.OPEN_PGP, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP);
|
||||
}
|
||||
});
|
||||
|
||||
restorePGP.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
replace = v.findViewById(R.id.backup_replace);
|
||||
break;
|
||||
}
|
||||
|
||||
backupType = type;
|
||||
settings.setDefaultBackupType(type);
|
||||
}
|
||||
|
||||
// End with a result
|
||||
|
@ -221,6 +259,38 @@ public class BackupActivity extends BaseActivity {
|
|||
pgpServiceConnection.unbindFromService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackupFinished() {
|
||||
hideProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackupFailed() {
|
||||
hideProgress();
|
||||
}
|
||||
|
||||
private void showProgress(boolean restore) {
|
||||
btnBackup.setEnabled(false);
|
||||
btnRestore.setEnabled(false);
|
||||
chkOldFormat.setEnabled(false);
|
||||
swReplace.setEnabled(false);
|
||||
|
||||
if (restore)
|
||||
progressRestore.setVisibility(View.VISIBLE);
|
||||
else
|
||||
progressBackup.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void hideProgress() {
|
||||
btnBackup.setEnabled(true);
|
||||
btnRestore.setEnabled(true);
|
||||
chkOldFormat.setEnabled(true);
|
||||
swReplace.setEnabled(true);
|
||||
|
||||
progressRestore.setVisibility(View.GONE);
|
||||
progressBackup.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Get the result from external activities
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
@ -293,26 +363,11 @@ public class BackupActivity extends BaseActivity {
|
|||
} else {
|
||||
if (settings.isBackupLocationSet()) {
|
||||
if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN) {
|
||||
BackupHelper.BackupFile plainBackupFile = BackupHelper.backupFile(this, settings.getBackupLocation(), Constants.BackupType.PLAIN_TEXT);
|
||||
|
||||
if (plainBackupFile.file != null)
|
||||
doBackupPlain(plainBackupFile.file.getUri());
|
||||
else
|
||||
Toast.makeText(this, plainBackupFile.errorMessage, Toast.LENGTH_LONG).show();
|
||||
doBackupPlain(null);
|
||||
} else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT) {
|
||||
BackupHelper.BackupFile cryptBackupFile = BackupHelper.backupFile(this, settings.getBackupLocation(), Constants.BackupType.ENCRYPTED);
|
||||
|
||||
if (cryptBackupFile.file != null)
|
||||
doBackupCrypt(cryptBackupFile.file.getUri());
|
||||
else
|
||||
Toast.makeText(this, cryptBackupFile.errorMessage, Toast.LENGTH_LONG).show();
|
||||
doBackupCrypt(null);
|
||||
} else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP) {
|
||||
BackupHelper.BackupFile pgpBackupFile = BackupHelper.backupFile(this, settings.getBackupLocation(), Constants.BackupType.OPEN_PGP);
|
||||
|
||||
if (pgpBackupFile.file != null)
|
||||
backupEncryptedWithPGP(pgpBackupFile.file.getUri(), null);
|
||||
else
|
||||
Toast.makeText(this, pgpBackupFile.errorMessage, Toast.LENGTH_LONG).show();
|
||||
backupEncryptedWithPGP(null, null);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_no_location, Toast.LENGTH_LONG).show();
|
||||
|
@ -324,7 +379,7 @@ public class BackupActivity extends BaseActivity {
|
|||
ArrayList<Entry> entries = DatabaseHelper.stringToEntries(text);
|
||||
|
||||
if (entries.size() > 0) {
|
||||
if (! replace.isChecked()) {
|
||||
if (! swReplace.isChecked()) {
|
||||
ArrayList<Entry> currentEntries = DatabaseHelper.loadDatabase(this, encryptionKey);
|
||||
|
||||
entries.removeAll(currentEntries);
|
||||
|
@ -360,14 +415,17 @@ public class BackupActivity extends BaseActivity {
|
|||
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey);
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
BackupHelper.SaveStringToFile runnable = new BackupHelper.SaveStringToFile(this, uri, DatabaseHelper.entriesToString(entries));
|
||||
|
||||
executor.execute(runnable);
|
||||
BackupTask backupTask = new BackupTask(this, Constants.BackupType.PLAIN_TEXT, DatabaseHelper.entriesToString(entries), this);
|
||||
|
||||
if (uri != null)
|
||||
backupTask.setUri(uri);
|
||||
|
||||
showProgress(false);
|
||||
executor.execute(backupTask);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
finishWithResult();
|
||||
}
|
||||
|
||||
private void backupPlainWithWarning() {
|
||||
|
@ -466,12 +524,11 @@ public class BackupActivity extends BaseActivity {
|
|||
|
||||
private void doBackupCryptWithPassword(Uri uri, String password) {
|
||||
if (Tools.isExternalStorageWritable()) {
|
||||
BackupHelper.backupToFileAsync(this, uri, password, encryptionKey, false);
|
||||
showProgress(false);
|
||||
BackupHelper.backupToFileAsync(this, uri, password, encryptionKey, this, false);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
finishWithResult();
|
||||
}
|
||||
|
||||
/* OpenPGP backup functions */
|
||||
|
@ -492,14 +549,17 @@ public class BackupActivity extends BaseActivity {
|
|||
private void doBackupEncrypted(Uri uri, String data) {
|
||||
if (Tools.isExternalStorageWritable()) {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
BackupHelper.SaveStringToFile runnable = new BackupHelper.SaveStringToFile(this, uri, data);
|
||||
|
||||
executor.execute(runnable);
|
||||
BackupTask backupTask = new BackupTask(this, Constants.BackupType.OPEN_PGP, data, this);
|
||||
|
||||
if (uri != null)
|
||||
backupTask.setUri(uri);
|
||||
|
||||
showProgress(false);
|
||||
executor.execute(backupTask);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
finishWithResult();
|
||||
}
|
||||
|
||||
private void backupEncryptedWithPGP(Uri uri, Intent encryptIntent) {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package org.shadowice.flocke.andotp.Tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Utilities.BackupHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||
import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper;
|
||||
|
||||
public class BackupTask implements Runnable {
|
||||
final private Context context;
|
||||
final private String payload;
|
||||
final private Constants.BackupType type;
|
||||
final private BackupCallback callback;
|
||||
|
||||
final private Handler handler;
|
||||
|
||||
private boolean silent = false;
|
||||
private Uri uri = null;
|
||||
private String password = null;
|
||||
|
||||
public BackupTask(Context context, Constants.BackupType type, String payload, BackupCallback callback) {
|
||||
this.context = context;
|
||||
this.payload = payload;
|
||||
this.type = type;
|
||||
this.callback = callback;
|
||||
|
||||
this.handler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public void setUri(Uri uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setSilent(boolean silent) {
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean success;
|
||||
|
||||
if (uri == null)
|
||||
uri = getTargetUri();
|
||||
|
||||
if (uri == null)
|
||||
return;
|
||||
|
||||
if (type == Constants.BackupType.PLAIN_TEXT) {
|
||||
success = StorageAccessHelper.saveFile(context, uri, payload);
|
||||
} else if (type == Constants.BackupType.ENCRYPTED) {
|
||||
success = BackupHelper.backupToFile(context, uri, password, payload);
|
||||
} else if (type == Constants.BackupType.OPEN_PGP) {
|
||||
success = StorageAccessHelper.saveFile(context, uri, payload);
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success)
|
||||
onFinished();
|
||||
else
|
||||
onFailed();
|
||||
}
|
||||
|
||||
private Uri getTargetUri() {
|
||||
Settings settings = new Settings(context);
|
||||
|
||||
BackupHelper.BackupFile backupFile = BackupHelper.backupFile(context, settings.getBackupLocation(), type);
|
||||
|
||||
if (backupFile.file != null) {
|
||||
return backupFile.file.getUri();
|
||||
} else {
|
||||
showToast(context, backupFile.errorMessage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showToast(Context context, int msgId) {
|
||||
if (!this.silent) {
|
||||
this.handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, msgId, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void onFinished() {
|
||||
this.handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, R.string.backup_toast_export_success, Toast.LENGTH_LONG).show();
|
||||
if (callback != null)
|
||||
callback.onBackupFinished();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onFailed() {
|
||||
this.handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, R.string.backup_toast_export_failed, Toast.LENGTH_LONG).show();
|
||||
if (callback != null)
|
||||
callback.onBackupFailed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface BackupCallback {
|
||||
void onBackupFinished();
|
||||
void onBackupFailed();
|
||||
}
|
||||
}
|
|
@ -2,14 +2,12 @@ package org.shadowice.flocke.andotp.Utilities;
|
|||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import org.shadowice.flocke.andotp.Database.Entry;
|
||||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Tasks.BackupTask;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -106,25 +104,31 @@ public class BackupHelper {
|
|||
return Constants.BackupType.UNAVAILABLE;
|
||||
}
|
||||
|
||||
public static void backupToFileAsync(Context context, Uri uri, String password, SecretKey encryptionKey, boolean silent) {
|
||||
public static void backupToFileAsync(Context context, Uri uri, String password, SecretKey encryptionKey, BackupTask.BackupCallback callback, boolean silent) {
|
||||
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(context, encryptionKey);
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
BackupCrypt runnable = new BackupCrypt(context, uri, password, entries, silent);
|
||||
|
||||
executor.execute(runnable);
|
||||
BackupTask backupTask = new BackupTask(context, Constants.BackupType.ENCRYPTED, DatabaseHelper.entriesToString(entries), callback);
|
||||
|
||||
if (uri != null)
|
||||
backupTask.setUri(uri);
|
||||
|
||||
backupTask.setPassword(password);
|
||||
backupTask.setSilent(silent);
|
||||
|
||||
executor.execute(backupTask);
|
||||
}
|
||||
|
||||
public static boolean backupToFile(Context context, Uri uri, String password, SecretKey encryptionKey) {
|
||||
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(context, encryptionKey);
|
||||
return backupToFile(context, uri, password, entries);
|
||||
}
|
||||
|
||||
public static boolean backupToFile(Context context, Uri uri, String password, ArrayList<Entry> entries)
|
||||
{
|
||||
|
||||
String plain = DatabaseHelper.entriesToString(entries);
|
||||
|
||||
return backupToFile(context, uri, password, plain);
|
||||
}
|
||||
|
||||
public static boolean backupToFile(Context context, Uri uri, String password, String plain)
|
||||
{
|
||||
boolean success = true;
|
||||
|
||||
try {
|
||||
|
@ -149,73 +153,4 @@ public class BackupHelper {
|
|||
|
||||
return success;
|
||||
}
|
||||
|
||||
public static class BackupCrypt implements Runnable {
|
||||
final private Context context;
|
||||
final private Uri uri;
|
||||
final private String password;
|
||||
final private ArrayList<Entry> entries;
|
||||
final private boolean silent;
|
||||
|
||||
public BackupCrypt(Context context, Uri uri, String password, ArrayList<Entry> entries, boolean silent) {
|
||||
this.context = context;
|
||||
this.uri = uri;
|
||||
this.password = password;
|
||||
this.entries = entries;
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean success = backupToFile(context, uri, password, entries);
|
||||
|
||||
if (!silent) {
|
||||
if (success) {
|
||||
postMessage(R.string.backup_toast_export_success);
|
||||
} else {
|
||||
postMessage(R.string.backup_toast_export_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void postMessage(int msgId) {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, msgId, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class SaveStringToFile implements Runnable {
|
||||
final private Context context;
|
||||
final private Uri uri;
|
||||
final private String data;
|
||||
|
||||
public SaveStringToFile(Context context, Uri uri, String data) {
|
||||
this.context = context;
|
||||
this.uri = uri;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean success = StorageAccessHelper.saveFile(context, uri, data);
|
||||
|
||||
if (success)
|
||||
postMessage(R.string.backup_toast_export_success);
|
||||
else
|
||||
postMessage(R.string.backup_toast_export_failed);
|
||||
}
|
||||
|
||||
void postMessage(int msgId) {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, msgId, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -605,4 +605,13 @@ public class Settings {
|
|||
public boolean getBlockAutofill() {
|
||||
return getBoolean(R.string.settings_key_block_autofill, false);
|
||||
}
|
||||
|
||||
public void setDefaultBackupType(Constants.BackupType type) {
|
||||
setString(R.string.settings_key_backup_default_type, type.name().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public Constants.BackupType getDefaultBackupType() {
|
||||
String defaultType = getString(R.string.settings_key_backup_default_type, Constants.BackupType.ENCRYPTED.name());
|
||||
return Constants.BackupType.valueOf(defaultType.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
|
@ -11,213 +10,70 @@
|
|||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/activity_margin"
|
||||
android:layout_marginTop="@dimen/activity_margin_large"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/activity_margin"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/backup_category_plain" />
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem"
|
||||
android:text="@string/backup_label_type"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_backup_plain"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
<Spinner
|
||||
android:id="@+id/backupType"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_export_plain" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_export_plain"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_restore_plain"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_import_plain" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_import_plain"/>
|
||||
android:entries="@array/backup_list_type_names" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/backupLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/activity_margin"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/backup_category_crypt" />
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:text="@string/backup_label_warning_plain"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msg_crypt_setup"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/backupErrorLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:visibility="gone"
|
||||
android:text="@string/backup_desc_crypt_setup"/>
|
||||
android:textColor="?attr/colorExpiring"
|
||||
android:text="@string/backup_desc_openpgp_provider"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_backup_crypt"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
<Button
|
||||
android:id="@+id/buttonBackup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_export_crypt" />
|
||||
android:text="@string/backup_button_backup"/>
|
||||
|
||||
<TextView
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarBackup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_export_crypt"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_restore_crypt"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_import_crypt" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_import_crypt"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_restore_crypt_old"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_import_crypt_old" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_import_crypt_old"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/activity_margin"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/backup_category_openpgp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msg_openpgp_setup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:visibility="gone"
|
||||
android:text="@string/backup_desc_openpgp_provider"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_backup_openpgp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_export_openpgp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_export_openpgp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_restore_openpgp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_import_openpgp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_import_openpgp"/>
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:indeterminate="true"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:layout_marginTop="@dimen/activity_margin_large"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
|
@ -242,7 +98,7 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/backup_replace"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -252,5 +108,30 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/restoreOldCrypt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/activity_margin"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:text="@string/backup_check_restore_old"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonRestore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/backup_button_restore" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarRestore"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:indeterminate="true"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
|
@ -53,6 +53,7 @@
|
|||
<string name="settings_key_openpgp_key_sign" translatable="false">pref_openpgp_key_sign</string>
|
||||
<string name="settings_key_openpgp_verify" translatable="false">pref_openpgp_verify</string>
|
||||
<string name="settings_key_new_backup_format_dialog_shown" translatable="false">pref_new_backup_dialog_shown</string> <!--Deprecated -->
|
||||
<string name="settings_key_backup_default_type" translatable="false">pref_backup_default_type</string>
|
||||
|
||||
<string name="settings_key_security_backup_warning" translatable="false">pref_security_backup_warning_shown</string>
|
||||
<string name="settings_key_sort_mode" translatable="false">pref_sort_mode</string>
|
||||
|
|
|
@ -2,33 +2,10 @@
|
|||
<resources>
|
||||
<string name="backup_activity_title">Backups</string>
|
||||
|
||||
<string name="backup_category_plain">Plain-text backups</string>
|
||||
<string name="backup_category_crypt">Encrypted backups</string>
|
||||
<string name="backup_category_openpgp">OpenPGP backups</string>
|
||||
|
||||
<string name="backup_title_export_plain">Backup (plain-text)</string>
|
||||
<string name="backup_title_export_crypt">Backup (encrypted)</string>
|
||||
<string name="backup_title_export_openpgp">Backup (OpenPGP)</string>
|
||||
<string name="backup_title_import_plain">Restore (plain-text)</string>
|
||||
<string name="backup_title_import_crypt">Restore (encrypted)</string>
|
||||
<string name="backup_title_import_crypt_old">Restore (encrypted, old encryption)</string>
|
||||
<string name="backup_title_import_openpgp">Restore (OpenPGP)</string>
|
||||
|
||||
<string name="backup_title_replace">Replace existing entries</string>
|
||||
|
||||
<string name="backup_desc_export_plain">Backup all accounts in a plain-text JSON file</string>
|
||||
<string name="backup_desc_export_crypt">Backup all accounts in a password-protected JSON file</string>
|
||||
<string name="backup_desc_export_openpgp">Backup all accounts in an OpenPGP-encrypted JSON file</string>
|
||||
<string name="backup_desc_import_plain">Restore accounts from a plain-text JSON file</string>
|
||||
<string name="backup_desc_import_crypt">Restore accounts from a password-protected JSON file</string>
|
||||
<string name="backup_desc_import_crypt_old">Restore accounts from a password-protected JSON file
|
||||
created with an <b>andOTP version lower than 0.6.3</b></string>
|
||||
<string name="backup_desc_import_openpgp">Restore accounts from an OpenPGP-encrypted JSON file</string>
|
||||
|
||||
<string name="backup_desc_crypt_setup">Failed to load the backup password from the <b>Settings</b>,
|
||||
this either means no password was set or something went wrong. You will be asked to enter
|
||||
the password manually when creating or importing a backup.
|
||||
</string>
|
||||
<string name="backup_desc_replace">If enabled all old entries are replaced when importing a
|
||||
backup and only the backup is present. If disabled the old entries and the backups content
|
||||
are merged.</string>
|
||||
|
||||
<string name="backup_desc_openpgp_provider">You need to install an OpenPGP provider and enable
|
||||
it in the <b>Settings</b> to use this feature.
|
||||
|
@ -37,18 +14,12 @@
|
|||
before you can create encrypted backups.
|
||||
</string>
|
||||
|
||||
<string name="backup_desc_replace">If enabled all old entries are replaced when importing a
|
||||
backup and only the backup is present. If disabled the old entries and the backups content
|
||||
are merged.</string>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<string name="backup_dialog_title_security_warning">Security warning</string>
|
||||
|
||||
<string name="backup_dialog_msg_export_warning">Do you really want to export the database as
|
||||
plain-text JSON file? This file contains all your secret keys, please <b>keep it safe</b>!
|
||||
</string>
|
||||
|
||||
|
||||
<string name="backup_receiver_title_backup_failed">Backup failed</string>
|
||||
<string name="backup_receiver_title_backup_success">Backup successful</string>
|
||||
|
||||
|
@ -89,4 +60,32 @@
|
|||
<string name="backup_toast_openpgp_not_verified">No verified signature detected</string>
|
||||
<string name="backup_toast_crypt_password_not_set">Password not set, check the <b>Settings</b></string>
|
||||
<string name="backup_toast_file_selection_failed">Can\'t open file selection dialog!</string>
|
||||
|
||||
<string name="backup_label_type">Backup type: </string>
|
||||
|
||||
<string name="backup_label_warning_plain">The backup will be stored in an unencrypted plain-text
|
||||
file. Please only use if strictly necessary and <b>keep the file safe</b>!</string>
|
||||
<string name="backup_label_crypt">The backup will be stored encrypted with a custom password
|
||||
using AES 256-bit encryption. You can setup a default password in the settings if you
|
||||
don\'t want to have to enter it every time you create a backup.</string>
|
||||
<string name="backup_label_pgp">The backup will be stored encrypted using the PGP key you
|
||||
specified in the settings. The encryption will be handled by a 3rd party app.</string>
|
||||
|
||||
<string name="backup_check_restore_old">Restore old backup format (created with an andOTP version
|
||||
before 0.6.3)</string>
|
||||
|
||||
<string name="backup_button_backup">Create backup</string>
|
||||
<string name="backup_button_restore">Restore backup</string>
|
||||
|
||||
<string-array name="backup_list_type_names">
|
||||
<item>Plain-text</item>
|
||||
<item>Encrypted</item>
|
||||
<item>OpenPGP</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="backup_list_type_values" translatable="false">
|
||||
<item>plain</item>
|
||||
<item>crypt</item>
|
||||
<item>pgp</item>
|
||||
</string-array>
|
||||
</resources>
|
Loading…
Reference in a new issue