Use only one TaskFragment in the BackupActivity

This commit is contained in:
Jakob Nixdorf 2021-03-25 21:28:58 +01:00
parent 2bec1e8224
commit 1b4df88916
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
3 changed files with 89 additions and 95 deletions

View file

@ -64,6 +64,7 @@ import org.shadowice.flocke.andotp.Tasks.PGPBackupTask;
import org.shadowice.flocke.andotp.Tasks.PGPRestoreTask; import org.shadowice.flocke.andotp.Tasks.PGPRestoreTask;
import org.shadowice.flocke.andotp.Tasks.PlainTextBackupTask; import org.shadowice.flocke.andotp.Tasks.PlainTextBackupTask;
import org.shadowice.flocke.andotp.Tasks.PlainTextRestoreTask; import org.shadowice.flocke.andotp.Tasks.PlainTextRestoreTask;
import org.shadowice.flocke.andotp.Tasks.UiBasedBackgroundTask;
import org.shadowice.flocke.andotp.Utilities.BackupHelper; import org.shadowice.flocke.andotp.Utilities.BackupHelper;
import org.shadowice.flocke.andotp.Utilities.Constants; import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper; import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
@ -81,8 +82,7 @@ import javax.crypto.SecretKey;
public class BackupActivity extends BaseActivity { public class BackupActivity extends BaseActivity {
private final static String TAG = BackupActivity.class.getSimpleName(); private final static String TAG = BackupActivity.class.getSimpleName();
private static final String TAG_BACKUP_TASK_FRAGMENT = "BackupActivity.BackupTaskFragmentTag"; private static final String TAG_TASK_FRAGMENT = "BackupActivity.TaskFragment";
private static final String TAG_RESTORE_TASK_FRAGMENT = "BackupActivity.RestoreTaskFragmentTag";
private Constants.BackupType backupType = Constants.BackupType.ENCRYPTED; private Constants.BackupType backupType = Constants.BackupType.ENCRYPTED;
private SecretKey encryptionKey = null; private SecretKey encryptionKey = null;
@ -286,10 +286,11 @@ public class BackupActivity extends BaseActivity {
notifyBackupState(R.string.backup_toast_export_failed); notifyBackupState(R.string.backup_toast_export_failed);
// Clean up the task fragment // Clean up the task fragment
BackupTaskFragment backupTaskFragment = findBackupTaskFragment(); TaskFragment taskFragment = findTaskFragment();
if (backupTaskFragment != null) {
if (taskFragment != null && taskFragment.isEmpty()) {
getFragmentManager().beginTransaction() getFragmentManager().beginTransaction()
.remove(backupTaskFragment) .remove(taskFragment)
.commit(); .commit();
} }
@ -320,10 +321,10 @@ public class BackupActivity extends BaseActivity {
showRestoreProgress(false); showRestoreProgress(false);
// Clean up the task fragment // Clean up the task fragment
RestoreTaskFragment restoreTaskFragment = findRestoreTaskFragment(); TaskFragment taskFragment = findTaskFragment();
if (restoreTaskFragment != null) { if (taskFragment != null && taskFragment.isEmpty()) {
getFragmentManager().beginTransaction() getFragmentManager().beginTransaction()
.remove(restoreTaskFragment) .remove(taskFragment)
.commit(); .commit();
} }
@ -607,7 +608,7 @@ public class BackupActivity extends BaseActivity {
if (settings.getOpenPGPVerify()) { if (settings.getOpenPGPVerify()) {
OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
if (sigResult.getResult() == OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED) { if (sigResult != null && sigResult.getResult() == OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED) {
restoreEntries(outputStreamToString(os), true); restoreEntries(outputStreamToString(os), true);
} else { } else {
Toast.makeText(this, R.string.backup_toast_openpgp_not_verified, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.backup_toast_openpgp_not_verified, Toast.LENGTH_LONG).show();
@ -620,6 +621,10 @@ public class BackupActivity extends BaseActivity {
} else if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED) { } else if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED) {
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
// Can't do anything without this intent
if (pi == null)
return;
// Small hack to keep the target file even after user interaction // Small hack to keep the target file even after user interaction
if (requestCode == Constants.INTENT_BACKUP_ENCRYPT_PGP) { if (requestCode == Constants.INTENT_BACKUP_ENCRYPT_PGP) {
encryptTargetFile = file; encryptTargetFile = file;
@ -634,89 +639,70 @@ public class BackupActivity extends BaseActivity {
} }
} else if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == OpenPgpApi.RESULT_CODE_ERROR) { } else if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == OpenPgpApi.RESULT_CODE_ERROR) {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
Toast.makeText(this, String.format(getString(R.string.backup_toast_openpgp_error), error.getMessage()), Toast.LENGTH_LONG).show();
if (error != null)
Toast.makeText(this, String.format(getString(R.string.backup_toast_openpgp_error), error.getMessage()), Toast.LENGTH_LONG).show();
} }
} }
@Nullable @Nullable
private BackupTaskFragment findBackupTaskFragment() { private TaskFragment findTaskFragment() {
return (BackupTaskFragment) getFragmentManager().findFragmentByTag(TAG_BACKUP_TASK_FRAGMENT); return (TaskFragment) getFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT);
}
@Nullable
private RestoreTaskFragment findRestoreTaskFragment() {
return (RestoreTaskFragment) getFragmentManager().findFragmentByTag(TAG_RESTORE_TASK_FRAGMENT);
} }
private void startBackupTask(GenericBackupTask task) { private void startBackupTask(GenericBackupTask task) {
BackupTaskFragment backupTaskFragment = findBackupTaskFragment(); TaskFragment taskFragment = findTaskFragment();
RestoreTaskFragment restoreTaskFragment = findRestoreTaskFragment();
// Don't start a task if we already have an active task running (backup or restore). // Don't start a task if we already have an active task running (backup or restore).
if ((backupTaskFragment == null || backupTaskFragment.task.isCanceled()) && (restoreTaskFragment == null || restoreTaskFragment.task.isCanceled())) { if (taskFragment == null || taskFragment.isEmpty()) {
if (backupTaskFragment == null) { if (taskFragment == null) {
backupTaskFragment = new BackupTaskFragment(); taskFragment = new TaskFragment();
getFragmentManager() getFragmentManager()
.beginTransaction() .beginTransaction()
.add(backupTaskFragment, TAG_BACKUP_TASK_FRAGMENT) .add(taskFragment, TAG_TASK_FRAGMENT)
.commit(); .commit();
} }
backupTaskFragment.startTask(task); taskFragment.startBackupTask(task);
showBackupProgress(true); showBackupProgress(true);
} }
} }
private void startRestoreTask(GenericRestoreTask task) { private void startRestoreTask(GenericRestoreTask task) {
BackupTaskFragment backupTaskFragment = findBackupTaskFragment(); TaskFragment taskFragment = findTaskFragment();
RestoreTaskFragment restoreTaskFragment = findRestoreTaskFragment();
// Don't start a task if we already have an active task running (backup or restore). // Don't start a task if we already have an active task running (backup or restore).
if ((backupTaskFragment == null || backupTaskFragment.task.isCanceled()) && (restoreTaskFragment == null || restoreTaskFragment.task.isCanceled())) { if (taskFragment == null || taskFragment.isEmpty()) {
if (restoreTaskFragment == null) { if (taskFragment == null) {
restoreTaskFragment = new RestoreTaskFragment(); taskFragment = new TaskFragment();
getFragmentManager() getFragmentManager()
.beginTransaction() .beginTransaction()
.add(restoreTaskFragment, TAG_RESTORE_TASK_FRAGMENT) .add(taskFragment, TAG_TASK_FRAGMENT)
.commit(); .commit();
} }
restoreTaskFragment.startTask(task); taskFragment.startRestoreTask(task);
showRestoreProgress(true); showRestoreProgress(true);
} }
} }
private void checkBackgroundBackupTask() { private void checkBackgroundTask() {
BackupTaskFragment backupTaskFragment = findBackupTaskFragment(); TaskFragment taskFragment = findTaskFragment();
if (backupTaskFragment != null) { if (taskFragment != null) {
if (backupTaskFragment.task.isCanceled()) { if (taskFragment.isEmpty()) {
// The task was canceled or has finished, so remove the task fragment. // The task was canceled or has finished, so remove the task fragment.
getFragmentManager().beginTransaction() getFragmentManager().beginTransaction()
.remove(backupTaskFragment) .remove(taskFragment)
.commit(); .commit();
} else { } else {
backupTaskFragment.task.setCallback(this::handleBackupTaskResult); if (taskFragment.isRestoreRunning())
showBackupProgress(true); showRestoreProgress(true);
}
}
} if (taskFragment.isBackupRunning())
showBackupProgress(true);
private void checkBackgroundRestoreTask() { taskFragment.setCallbacks(this::handleBackupTaskResult, this::handleRestoreTaskResult);
RestoreTaskFragment restoreTaskFragment = findRestoreTaskFragment();
if (restoreTaskFragment != null) {
if (restoreTaskFragment.task.isCanceled()) {
// The task was canceled or has finished, so remove the task fragment.
getFragmentManager().beginTransaction()
.remove(restoreTaskFragment)
.commit();
} else {
restoreTaskFragment.task.setCallback(this::handleRestoreTaskResult);
showRestoreProgress(true);
} }
} }
} }
@ -726,22 +712,16 @@ public class BackupActivity extends BaseActivity {
super.onPause(); super.onPause();
// We don't want the task to callback to a dead activity and cause a memory leak, so null it here. // We don't want the task to callback to a dead activity and cause a memory leak, so null it here.
BackupTaskFragment backupTaskFragment = findBackupTaskFragment(); TaskFragment taskFragment = findTaskFragment();
RestoreTaskFragment restoreTaskFragment = findRestoreTaskFragment();
if (backupTaskFragment != null) if (taskFragment != null)
backupTaskFragment.task.setCallback(null); taskFragment.setCallbacks(null, null);
if (restoreTaskFragment != null)
restoreTaskFragment.task.setCallback(null);
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
checkBackgroundTask();
checkBackgroundBackupTask();
checkBackgroundRestoreTask();
} }
@Override @Override
@ -749,33 +729,43 @@ public class BackupActivity extends BaseActivity {
return allowExit; // Don't destroy the backup activity as long as a backup task is running return allowExit; // Don't destroy the backup activity as long as a backup task is running
} }
/** Retained instance fragment to hold a running {@link GenericBackupTask} between configuration changes.*/ public static class TaskFragment extends Fragment {
public static class BackupTaskFragment extends Fragment { GenericBackupTask backupTask;
GenericBackupTask task; GenericRestoreTask restoreTask;
public BackupTaskFragment() { public TaskFragment() {
super(); super();
setRetainInstance(true); setRetainInstance(true);
} }
public void startTask(@NonNull GenericBackupTask task) { public void setCallbacks(UiBasedBackgroundTask.UiCallback<GenericBackupTask.BackupTaskResult> backupCallback, UiBasedBackgroundTask.UiCallback<GenericRestoreTask.RestoreTaskResult> restoreCallback) {
this.task = task; if (backupTask != null && backupTask.isRunning())
this.task.execute(); backupTask.setCallback(backupCallback);
}
}
/** Retained instance fragment to hold a running {@link GenericRestoreTask} between configuration changes.*/ if (restoreTask != null && restoreTask.isRunning())
public static class RestoreTaskFragment extends Fragment { restoreTask.setCallback(restoreCallback);
GenericRestoreTask task;
public RestoreTaskFragment() {
super();
setRetainInstance(true);
} }
public void startTask(@NonNull GenericRestoreTask task) { public boolean isEmpty() {
this.task = task; return ((backupTask == null || !backupTask.isRunning()) && (restoreTask == null || !restoreTask.isRunning()));
this.task.execute(); }
public boolean isRestoreRunning() {
return (restoreTask != null && restoreTask.isRunning());
}
public boolean isBackupRunning() {
return (backupTask != null && backupTask.isRunning());
}
public void startBackupTask(@NonNull GenericBackupTask task) {
this.backupTask = task;
this.backupTask.execute();
}
public void startRestoreTask(@NonNull GenericRestoreTask task) {
this.restoreTask = task;
this.restoreTask.execute();
} }
} }
} }

View file

@ -44,7 +44,6 @@ import androidx.appcompat.widget.Toolbar;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.util.Log; import android.util.Log;
import android.view.MenuItem;
import android.view.ViewStub; import android.view.ViewStub;
import android.widget.Toast; import android.widget.Toast;
@ -121,14 +120,6 @@ public class SettingsActivity extends BaseActivity
prefs.registerOnSharedPreferenceChangeListener(this); prefs.registerOnSharedPreferenceChangeListener(this);
} }
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home && !canGoBack)
return true;
return super.onOptionsItemSelected(item);
}
@Override @Override
protected void onSaveInstanceState(@NonNull Bundle outState) { protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
@ -150,7 +141,9 @@ public class SettingsActivity extends BaseActivity
@Override @Override
public boolean onSupportNavigateUp() { public boolean onSupportNavigateUp() {
finishWithResult(); if (canGoBack)
finishWithResult();
return true; return true;
} }

View file

@ -27,6 +27,7 @@ public abstract class UiBasedBackgroundTask<Result> {
private Result awaitedResult; private Result awaitedResult;
private volatile boolean isCanceled = false; private volatile boolean isCanceled = false;
private volatile boolean isRunning = false;
/** @param failedResult The result to return if the task fails (throws an exception or returns null). */ /** @param failedResult The result to return if the task fails (throws an exception or returns null). */
public UiBasedBackgroundTask(@NonNull Result failedResult) { public UiBasedBackgroundTask(@NonNull Result failedResult) {
@ -52,7 +53,10 @@ public abstract class UiBasedBackgroundTask<Result> {
} }
private void emitResultOnMainThread(@NonNull UiCallback<Result> callback, @NonNull Result result) { private void emitResultOnMainThread(@NonNull UiCallback<Result> callback, @NonNull Result result) {
mainThreadHandler.post(() -> callback.onResult(result)); mainThreadHandler.post(() -> {
callback.onResult(result);
isRunning = false;
});
this.callback = null; this.callback = null;
this.awaitedResult = null; this.awaitedResult = null;
} }
@ -60,6 +64,7 @@ public abstract class UiBasedBackgroundTask<Result> {
/** Executed the task on a background thread. Safe to call from the main thread. */ /** Executed the task on a background thread. Safe to call from the main thread. */
@AnyThread @AnyThread
public void execute() { public void execute() {
isRunning = true;
executor.execute(this::runTask); executor.execute(this::runTask);
} }
@ -96,9 +101,15 @@ public abstract class UiBasedBackgroundTask<Result> {
return isCanceled; return isCanceled;
} }
@AnyThread
public boolean isRunning() {
return isRunning;
}
@AnyThread @AnyThread
public void cancel() { public void cancel() {
isCanceled = true; isCanceled = true;
isRunning = false;
} }
@FunctionalInterface @FunctionalInterface