Create generic activity class to simplify handling background tasks

This commit is contained in:
Jakob Nixdorf 2021-03-27 18:22:23 +01:00
parent 1b4df88916
commit faa8fb3774
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
14 changed files with 333 additions and 472 deletions

View file

@ -22,19 +22,13 @@
package org.shadowice.flocke.andotp.Activities; package org.shadowice.flocke.andotp.Activities;
import android.app.Fragment;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
@ -59,17 +53,14 @@ import org.shadowice.flocke.andotp.View.AutoFillable.AutoFillableTextInputEditTe
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod; import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
public class AuthenticateActivity extends BaseActivity public class AuthenticateActivity extends BackgroundTaskActivity<AuthenticationTask.Result>
implements EditText.OnEditorActionListener, View.OnClickListener { implements EditText.OnEditorActionListener, View.OnClickListener {
private final AutoFillableTextInputEditText.AutoFillTextListener autoFillTextListener = text -> startAuthTask(text.toString()); private final AutoFillableTextInputEditText.AutoFillTextListener autoFillTextListener = text -> startAuthTask(text.toString());
private static final String TAG_TASK_FRAGMENT = "AuthenticateActivity.TaskFragmentTag";
private AuthMethod authMethod; private AuthMethod authMethod;
private String newEncryption = ""; private String newEncryption = "";
private String existingAuthCredentials; private String existingAuthCredentials;
private boolean isAuthUpgrade = false; private boolean isAuthUpgrade = false;
private ProcessLifecycleObserver observer;
private TextInputLayout passwordLayout; private TextInputLayout passwordLayout;
AutoFillableTextInputEditText passwordInput; AutoFillableTextInputEditText passwordInput;
@ -113,10 +104,6 @@ public class AuthenticateActivity extends BaseActivity
} }
}); });
observer = new ProcessLifecycleObserver();
ProcessLifecycleOwner.get().getLifecycle()
.addObserver(observer);
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
} }
@ -171,58 +158,21 @@ public class AuthenticateActivity extends BaseActivity
unlockProgress = v.findViewById(R.id.unlockProgress); unlockProgress = v.findViewById(R.id.unlockProgress);
} }
private void cancelBackgroundTask() { @Override
TaskFragment taskFragment = findTaskFragment(); protected boolean cancelTaskOnScreenOff() {
if (taskFragment != null) { return true;
taskFragment.task.cancel();
}
setupUiForTaskState(false);
}
private class ProcessLifecycleObserver implements DefaultLifecycleObserver {
@Override
public void onStop(@NonNull LifecycleOwner owner) {
if (settings.getRelockOnBackground()) {
cancelBackgroundTask();
}
}
} }
@Override @Override
public void onResume() { protected void onReturnToCanceledTask() {
super.onResume();
checkBackgroundTask();
}
private void checkBackgroundTask() {
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null) {
if (taskFragment.task.isCanceled()) {
// The task was canceled, so remove the task fragment and reset password input.
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
resetPasswordInput();
} else {
taskFragment.task.setCallback(this::handleResult);
setupUiForTaskState(true);
}
}
}
private void resetPasswordInput() {
passwordInput.setText(""); passwordInput.setText("");
passwordInput.requestFocus(); passwordInput.requestFocus();
InputMethodManager keyboard = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager keyboard = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
keyboard.showSoftInput(passwordInput, 0); keyboard.showSoftInput(passwordInput, 0);
} }
@Nullable @Override
private TaskFragment findTaskFragment() { protected void setupUiForTaskState(boolean isTaskRunning) {
return (TaskFragment) getFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT);
}
private void setupUiForTaskState(boolean isTaskRunning) {
passwordLayout.setEnabled(!isTaskRunning); passwordLayout.setEnabled(!isTaskRunning);
passwordInput.setEnabled(!isTaskRunning); passwordInput.setEnabled(!isTaskRunning);
unlockButton.setEnabled(!isTaskRunning); unlockButton.setEnabled(!isTaskRunning);
@ -246,25 +196,12 @@ public class AuthenticateActivity extends BaseActivity
} }
private void startAuthTask(String plainPassword) { private void startAuthTask(String plainPassword) {
TaskFragment taskFragment = findTaskFragment(); AuthenticationTask task = new AuthenticationTask(this, isAuthUpgrade, existingAuthCredentials, plainPassword);
// Don't start a task if we already have an active task running. startBackgroundTask(task);
if (taskFragment == null || taskFragment.task.isCanceled()) {
AuthenticationTask task = new AuthenticationTask(this, isAuthUpgrade, existingAuthCredentials, plainPassword);
task.setCallback(this::handleResult);
if (taskFragment == null) {
taskFragment = new TaskFragment();
getFragmentManager()
.beginTransaction()
.add(taskFragment, TAG_TASK_FRAGMENT)
.commit();
}
taskFragment.startTask(task);
setupUiForTaskState(true);
}
} }
private void handleResult(Result result) { @Override
void onTaskResult(Result result) {
if (result.authUpgradeFailed) { if (result.authUpgradeFailed) {
Toast.makeText(this, R.string.settings_toast_auth_upgrade_failed, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.settings_toast_auth_upgrade_failed, Toast.LENGTH_LONG).show();
} }
@ -296,47 +233,14 @@ public class AuthenticateActivity extends BaseActivity
} }
} }
@Override
protected void onPause() {
super.onPause();
// We don't want the task to callback to a dead activity and cause a memory leak, so null it here.
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null) {
taskFragment.task.setCallback(null);
}
}
@Override @Override
protected void onStop() { protected void onStop() {
passwordInput.setAutoFillTextListener(null); passwordInput.setAutoFillTextListener(null);
super.onStop(); super.onStop();
} }
@Override
protected void onDestroy() {
ProcessLifecycleOwner.get().getLifecycle()
.removeObserver(observer);
super.onDestroy();
}
@Override @Override
protected boolean shouldDestroyOnScreenOff() { protected boolean shouldDestroyOnScreenOff() {
return false; return false;
} }
/** Retained instance fragment to hold a running {@link AuthenticationTask} between configuration changes.*/
public static class TaskFragment extends Fragment {
AuthenticationTask task;
public TaskFragment() {
super();
setRetainInstance(true);
}
public void startTask(@NonNull AuthenticationTask task) {
this.task = task;
task.execute();
}
}
} }

View file

@ -0,0 +1,191 @@
package org.shadowice.flocke.andotp.Activities;
import android.app.Fragment;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import org.shadowice.flocke.andotp.Tasks.UiBasedBackgroundTask;
public abstract class BackgroundTaskActivity<Result> extends BaseActivity {
final protected String TAG_TASK_FRAGMENT = this.getClass().getSimpleName() + ".TaskFragment";
private ProcessLifecycleObserver observer = null;
abstract void onTaskResult(Result result);
protected void onReturnToCanceledTask() {
// This can be overwritten if we need to do something here
}
protected void setupUiForTaskState(boolean running) {
// This can be overwritten when we need to give some UI feedback
}
protected boolean cancelTaskOnScreenOff() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (cancelTaskOnScreenOff()) {
observer = new ProcessLifecycleObserver();
ProcessLifecycleOwner.get().getLifecycle()
.addObserver(observer);
}
}
@Override
protected void onDestroy() {
if (observer != null) {
ProcessLifecycleOwner.get().getLifecycle()
.removeObserver(observer);
observer = null;
}
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
checkBackgroundTask();
}
@Override
protected void onPause() {
super.onPause();
// We don't want the task to callback to a dead activity and cause a memory leak, so null it here.
TaskFragment<Result> taskFragment = findTaskFragment();
if (taskFragment != null)
taskFragment.setCallback(null);
}
protected void cancelBackgroundTask() {
TaskFragment<Result> taskFragment = findTaskFragment();
if (taskFragment != null)
taskFragment.cancelTask();
setupUiForTaskState(false);
}
protected void startBackgroundTask(UiBasedBackgroundTask<Result> task) {
TaskFragment<Result> taskFragment = findTaskFragment();
// Don't start a task if we already have an active task running.
if (taskFragment == null || taskFragment.isCanceled()) {
task.setCallback(this::handleTaskResult);
if (taskFragment == null) {
taskFragment = new TaskFragment<>();
getFragmentManager()
.beginTransaction()
.add(taskFragment, TAG_TASK_FRAGMENT)
.commit();
}
taskFragment.startTask(task);
setupUiForTaskState(true);
}
}
private void handleTaskResult(Result result) {
setupUiForTaskState(false);
onTaskResult(result);
// Remove the task fragment after the task is finished
TaskFragment<Result> taskFragment = findTaskFragment();
if (taskFragment != null) {
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
}
}
private void checkBackgroundTask() {
TaskFragment<Result> taskFragment = findTaskFragment();
if (taskFragment != null) {
if (taskFragment.isCanceled()) {
// The task was canceled, so remove the task fragment
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
onReturnToCanceledTask();
setupUiForTaskState(false);
} else {
taskFragment.setCallback(this::handleTaskResult);
setupUiForTaskState(true);
}
} else {
setupUiForTaskState(false);
}
}
@Nullable
@SuppressWarnings("unchecked")
private TaskFragment<Result> findTaskFragment() {
Fragment fragment = getFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT);
if (fragment instanceof TaskFragment)
return (TaskFragment<Result>) fragment;
else
return null;
}
private class ProcessLifecycleObserver implements DefaultLifecycleObserver {
@Override
public void onStop(@NonNull LifecycleOwner owner) {
if (cancelTaskOnScreenOff())
cancelBackgroundTask();
}
}
public static class TaskFragment<Result> extends Fragment {
private UiBasedBackgroundTask<Result> task;
public TaskFragment() {
super();
setRetainInstance(true);
}
public void startTask(@NonNull UiBasedBackgroundTask<Result> task) {
this.task = task;
task.execute();
}
public void setCallback(@Nullable UiBasedBackgroundTask.UiCallback<Result> callback) {
if (this.task != null)
this.task.setCallback(callback);
}
public boolean isCanceled() {
if (task != null)
return task.isCanceled();
else
return true;
}
public void cancelTask() {
if (task != null)
task.cancel();
}
}
}

View file

@ -23,7 +23,6 @@
package org.shadowice.flocke.andotp.Activities; package org.shadowice.flocke.andotp.Activities;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Fragment;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Intent; import android.content.Intent;
@ -31,8 +30,6 @@ import android.content.IntentSender;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import android.text.TextUtils; import android.text.TextUtils;
@ -56,10 +53,9 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection;
import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.Dialogs.PasswordEntryDialog; import org.shadowice.flocke.andotp.Dialogs.PasswordEntryDialog;
import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Tasks.BackupTaskResult;
import org.shadowice.flocke.andotp.Tasks.EncryptedBackupTask; import org.shadowice.flocke.andotp.Tasks.EncryptedBackupTask;
import org.shadowice.flocke.andotp.Tasks.EncryptedRestoreTask; import org.shadowice.flocke.andotp.Tasks.EncryptedRestoreTask;
import org.shadowice.flocke.andotp.Tasks.GenericBackupTask;
import org.shadowice.flocke.andotp.Tasks.GenericRestoreTask;
import org.shadowice.flocke.andotp.Tasks.PGPBackupTask; 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;
@ -79,11 +75,9 @@ import java.util.ArrayList;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
public class BackupActivity extends BaseActivity { public class BackupActivity extends BackgroundTaskActivity<BackupTaskResult> {
private final static String TAG = BackupActivity.class.getSimpleName(); private final static String TAG = BackupActivity.class.getSimpleName();
private static final String TAG_TASK_FRAGMENT = "BackupActivity.TaskFragment";
private Constants.BackupType backupType = Constants.BackupType.ENCRYPTED; private Constants.BackupType backupType = Constants.BackupType.ENCRYPTED;
private SecretKey encryptionKey = null; private SecretKey encryptionKey = null;
@ -105,6 +99,8 @@ public class BackupActivity extends BaseActivity {
private boolean reload = false; private boolean reload = false;
private boolean allowExit = true; private boolean allowExit = true;
private BackupTaskResult.ResultType currentTask = BackupTaskResult.ResultType.NONE;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -276,29 +272,28 @@ public class BackupActivity extends BaseActivity {
Toast.makeText(this, msgId, Toast.LENGTH_LONG).show(); Toast.makeText(this, msgId, Toast.LENGTH_LONG).show();
} }
private void handleBackupTaskResult(GenericBackupTask.BackupTaskResult result) { @Override
showBackupProgress(false); void onTaskResult(BackupTaskResult backupTaskResult) {
if (backupTaskResult.resultType == BackupTaskResult.ResultType.BACKUP)
handleBackupTaskResult(backupTaskResult);
else if (backupTaskResult.resultType == BackupTaskResult.ResultType.RESTORE)
handleRestoreTaskResult(backupTaskResult);
}
private void handleBackupTaskResult(BackupTaskResult result) {
if (result.messageId != 0) if (result.messageId != 0)
notifyBackupState(result.messageId); notifyBackupState(result.messageId);
else else
if (!result.success) if (result.success)
notifyBackupState(R.string.backup_toast_export_success);
else
notifyBackupState(R.string.backup_toast_export_failed); notifyBackupState(R.string.backup_toast_export_failed);
// Clean up the task fragment
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null && taskFragment.isEmpty()) {
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
}
if (result.success) if (result.success)
finishWithResult(); finishWithResult();
} }
private void handleRestoreTaskResult(GenericRestoreTask.RestoreTaskResult result) { private void handleRestoreTaskResult(BackupTaskResult result) {
if (result.success) { if (result.success) {
if (result.isPGP) { if (result.isPGP) {
InputStream is = new ByteArrayInputStream(result.payload.getBytes(StandardCharsets.UTF_8)); InputStream is = new ByteArrayInputStream(result.payload.getBytes(StandardCharsets.UTF_8));
@ -318,20 +313,23 @@ public class BackupActivity extends BaseActivity {
notifyBackupState(R.string.backup_toast_import_failed); notifyBackupState(R.string.backup_toast_import_failed);
} }
showRestoreProgress(false);
// Clean up the task fragment
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null && taskFragment.isEmpty()) {
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
}
if (result.success && !result.isPGP) if (result.success && !result.isPGP)
finishWithResult(); finishWithResult();
} }
@Override
protected void setupUiForTaskState(boolean running) {
if (currentTask == BackupTaskResult.ResultType.BACKUP)
showBackupProgress(running);
else if (currentTask == BackupTaskResult.ResultType.RESTORE)
showRestoreProgress(running);
}
protected void startBackgroundTask(UiBasedBackgroundTask<BackupTaskResult> task, BackupTaskResult.ResultType type) {
currentTask = type;
startBackgroundTask(task);
}
private void toggleInProgressMode(boolean running) { private void toggleInProgressMode(boolean running) {
allowExit = !running; allowExit = !running;
@ -465,9 +463,7 @@ public class BackupActivity extends BaseActivity {
private void doRestorePlain(Uri uri) { private void doRestorePlain(Uri uri) {
if (Tools.isExternalStorageReadable()) { if (Tools.isExternalStorageReadable()) {
PlainTextRestoreTask task = new PlainTextRestoreTask(this, uri); PlainTextRestoreTask task = new PlainTextRestoreTask(this, uri);
task.setCallback(this::handleRestoreTaskResult); startBackgroundTask(task, BackupTaskResult.ResultType.RESTORE);
startRestoreTask(task);
} else { } else {
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
} }
@ -478,9 +474,7 @@ public class BackupActivity extends BaseActivity {
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey); ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey);
PlainTextBackupTask task = new PlainTextBackupTask(this, entries, uri); PlainTextBackupTask task = new PlainTextBackupTask(this, entries, uri);
task.setCallback(this::handleBackupTaskResult); startBackgroundTask(task, BackupTaskResult.ResultType.BACKUP);
startBackupTask(task);
} else { } else {
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
} }
@ -514,9 +508,7 @@ public class BackupActivity extends BaseActivity {
private void doRestoreCryptWithPassword(Uri uri, String password, boolean old_format) { private void doRestoreCryptWithPassword(Uri uri, String password, boolean old_format) {
if (Tools.isExternalStorageReadable()) { if (Tools.isExternalStorageReadable()) {
EncryptedRestoreTask task = new EncryptedRestoreTask(this, uri, password, old_format); EncryptedRestoreTask task = new EncryptedRestoreTask(this, uri, password, old_format);
task.setCallback(this::handleRestoreTaskResult); startBackgroundTask(task, BackupTaskResult.ResultType.RESTORE);
startRestoreTask(task);
} else { } else {
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
} }
@ -538,9 +530,7 @@ public class BackupActivity extends BaseActivity {
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey); ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey);
EncryptedBackupTask task = new EncryptedBackupTask(this, entries, password, uri); EncryptedBackupTask task = new EncryptedBackupTask(this, entries, password, uri);
task.setCallback(this::handleBackupTaskResult); startBackgroundTask(task, BackupTaskResult.ResultType.BACKUP);
startBackupTask(task);
} else { } else {
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
} }
@ -553,17 +543,13 @@ public class BackupActivity extends BaseActivity {
decryptIntent = new Intent(OpenPgpApi.ACTION_DECRYPT_VERIFY); decryptIntent = new Intent(OpenPgpApi.ACTION_DECRYPT_VERIFY);
PGPRestoreTask task = new PGPRestoreTask(this, uri, decryptIntent); PGPRestoreTask task = new PGPRestoreTask(this, uri, decryptIntent);
task.setCallback(this::handleRestoreTaskResult); startBackgroundTask(task, BackupTaskResult.ResultType.RESTORE);
startRestoreTask(task);
} }
private void doBackupEncrypted(Uri uri, String data) { private void doBackupEncrypted(Uri uri, String data) {
if (Tools.isExternalStorageWritable()) { if (Tools.isExternalStorageWritable()) {
PGPBackupTask task = new PGPBackupTask(this, data, uri); PGPBackupTask task = new PGPBackupTask(this, data, uri);
task.setCallback(this::handleBackupTaskResult); startBackgroundTask(task, BackupTaskResult.ResultType.BACKUP);
startBackupTask(task);
} else { } else {
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
} }
@ -645,127 +631,8 @@ public class BackupActivity extends BaseActivity {
} }
} }
@Nullable
private TaskFragment findTaskFragment() {
return (TaskFragment) getFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT);
}
private void startBackupTask(GenericBackupTask task) {
TaskFragment taskFragment = findTaskFragment();
// Don't start a task if we already have an active task running (backup or restore).
if (taskFragment == null || taskFragment.isEmpty()) {
if (taskFragment == null) {
taskFragment = new TaskFragment();
getFragmentManager()
.beginTransaction()
.add(taskFragment, TAG_TASK_FRAGMENT)
.commit();
}
taskFragment.startBackupTask(task);
showBackupProgress(true);
}
}
private void startRestoreTask(GenericRestoreTask task) {
TaskFragment taskFragment = findTaskFragment();
// Don't start a task if we already have an active task running (backup or restore).
if (taskFragment == null || taskFragment.isEmpty()) {
if (taskFragment == null) {
taskFragment = new TaskFragment();
getFragmentManager()
.beginTransaction()
.add(taskFragment, TAG_TASK_FRAGMENT)
.commit();
}
taskFragment.startRestoreTask(task);
showRestoreProgress(true);
}
}
private void checkBackgroundTask() {
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null) {
if (taskFragment.isEmpty()) {
// The task was canceled or has finished, so remove the task fragment.
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
} else {
if (taskFragment.isRestoreRunning())
showRestoreProgress(true);
if (taskFragment.isBackupRunning())
showBackupProgress(true);
taskFragment.setCallbacks(this::handleBackupTaskResult, this::handleRestoreTaskResult);
}
}
}
@Override
protected void onPause() {
super.onPause();
// We don't want the task to callback to a dead activity and cause a memory leak, so null it here.
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null)
taskFragment.setCallbacks(null, null);
}
@Override
public void onResume() {
super.onResume();
checkBackgroundTask();
}
@Override @Override
protected boolean shouldDestroyOnScreenOff() { protected boolean shouldDestroyOnScreenOff() {
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
} }
public static class TaskFragment extends Fragment {
GenericBackupTask backupTask;
GenericRestoreTask restoreTask;
public TaskFragment() {
super();
setRetainInstance(true);
}
public void setCallbacks(UiBasedBackgroundTask.UiCallback<GenericBackupTask.BackupTaskResult> backupCallback, UiBasedBackgroundTask.UiCallback<GenericRestoreTask.RestoreTaskResult> restoreCallback) {
if (backupTask != null && backupTask.isRunning())
backupTask.setCallback(backupCallback);
if (restoreTask != null && restoreTask.isRunning())
restoreTask.setCallback(restoreCallback);
}
public boolean isEmpty() {
return ((backupTask == null || !backupTask.isRunning()) && (restoreTask == null || !restoreTask.isRunning()));
}
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

@ -22,7 +22,6 @@
package org.shadowice.flocke.andotp.Activities; package org.shadowice.flocke.andotp.Activities;
import android.app.Fragment;
import android.app.backup.BackupManager; import android.app.backup.BackupManager;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Intent; import android.content.Intent;
@ -39,13 +38,14 @@ import android.preference.PreferenceFragment;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.util.Log; import android.util.Log;
import android.view.ViewStub; import android.view.ViewStub;
import android.widget.Toast;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
import org.openintents.openpgp.util.OpenPgpAppPreference; import org.openintents.openpgp.util.OpenPgpAppPreference;
import org.openintents.openpgp.util.OpenPgpKeyPreference; import org.openintents.openpgp.util.OpenPgpKeyPreference;
@ -67,18 +67,16 @@ import javax.crypto.SecretKey;
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod; import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType; import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType;
public class SettingsActivity extends BaseActivity public class SettingsActivity extends BackgroundTaskActivity<ChangeEncryptionTask.Result>
implements SharedPreferences.OnSharedPreferenceChangeListener, EncryptionHelper.EncryptionChangeCallback { implements SharedPreferences.OnSharedPreferenceChangeListener, EncryptionHelper.EncryptionChangeCallback {
private static final String TAG_TASK_FRAGMENT = "SettingsActivity.TaskFragmentTag";
private SettingsFragment fragment; private SettingsFragment fragment;
private SharedPreferences prefs; private SharedPreferences prefs;
private SecretKey encryptionKey = null; private SecretKey encryptionKey = null;
private boolean encryptionChanged = false; private boolean encryptionChanged = false;
private Toast inProgress = null; private Snackbar inProgress = null;
private boolean canGoBack = true; private boolean canGoBack = true;
@Override @Override
@ -189,7 +187,7 @@ public class SettingsActivity extends BaseActivity
(settings.getAndroidBackupServiceEnabled() ? "enabled" : "disabled")); (settings.getAndroidBackupServiceEnabled() ? "enabled" : "disabled"));
int message = settings.getAndroidBackupServiceEnabled() ? R.string.settings_toast_android_sync_enabled : R.string.settings_toast_android_sync_disabled; int message = settings.getAndroidBackupServiceEnabled() ? R.string.settings_toast_android_sync_enabled : R.string.settings_toast_android_sync_disabled;
Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); Snackbar.make(fragment.getView(), message, BaseTransientBottomBar.LENGTH_SHORT).show();
} }
fragment.updateAutoBackup(); fragment.updateAutoBackup();
@ -218,106 +216,43 @@ public class SettingsActivity extends BaseActivity
fragment.setEncryptionKey(newEncryptionKey); fragment.setEncryptionKey(newEncryptionKey);
} }
private void handleTaskResult(ChangeEncryptionTask.Result result) { @Override
setupUiForTaskState(false); void onTaskResult(ChangeEncryptionTask.Result result) {
switch (result.result) { switch (result.result) {
case SUCCESS: case SUCCESS:
onSuccessfulEncryptionChange(result.encryptionType, result.newEncryptionKey); onSuccessfulEncryptionChange(result.encryptionType, result.newEncryptionKey);
Toast.makeText(this, R.string.settings_toast_encryption_change_success, Toast.LENGTH_LONG).show(); Snackbar.make(fragment.getView(), R.string.settings_toast_encryption_change_success, BaseTransientBottomBar.LENGTH_SHORT).show();
break; break;
case BACKUP_FAILURE: case BACKUP_FAILURE:
Toast.makeText(this, R.string.settings_toast_encryption_backup_failed, Toast.LENGTH_LONG).show(); Snackbar.make(fragment.getView(), R.string.settings_toast_encryption_backup_failed, BaseTransientBottomBar.LENGTH_SHORT).show();
break; break;
case CHANGE_FAILURE: case CHANGE_FAILURE:
Toast.makeText(this, R.string.settings_toast_encryption_change_failed, Toast.LENGTH_LONG).show(); Snackbar.make(fragment.getView(), R.string.settings_toast_encryption_change_failed, BaseTransientBottomBar.LENGTH_SHORT).show();
break; break;
} }
// Remove the task fragment after the task is finished
TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null) {
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
}
}
@Nullable
private SettingsActivity.TaskFragment findTaskFragment() {
return (SettingsActivity.TaskFragment) getFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT);
} }
private void startEncryptionChangeTask(EncryptionType newEnc, byte[] newKey) { private void startEncryptionChangeTask(EncryptionType newEnc, byte[] newKey) {
SettingsActivity.TaskFragment taskFragment = findTaskFragment(); ChangeEncryptionTask task = new ChangeEncryptionTask(this, encryptionKey, newEnc, newKey);
// Don't start a task if we already have an active task running. startBackgroundTask(task);
if (taskFragment == null || taskFragment.task.isCanceled()) {
ChangeEncryptionTask task = new ChangeEncryptionTask(this, encryptionKey, newEnc, newKey);
task.setCallback(this::handleTaskResult);
if (taskFragment == null) {
taskFragment = new SettingsActivity.TaskFragment();
getFragmentManager()
.beginTransaction()
.add(taskFragment, TAG_TASK_FRAGMENT)
.commit();
}
taskFragment.startTask(task);
setupUiForTaskState(true);
}
} }
private void checkBackgroundTask() { @Override
SettingsActivity.TaskFragment taskFragment = findTaskFragment(); protected void setupUiForTaskState(boolean taskRunning) {
if (taskFragment != null) {
if (taskFragment.task.isCanceled()) {
// The task was canceled, so remove the task fragment
getFragmentManager().beginTransaction()
.remove(taskFragment)
.commit();
setupUiForTaskState(false);
} else {
taskFragment.task.setCallback(this::handleTaskResult);
setupUiForTaskState(true);
}
} else {
setupUiForTaskState(false);
}
}
private void setupUiForTaskState(boolean taskRunning) {
if (taskRunning) { if (taskRunning) {
canGoBack = false;
// TODO: Better in-progress notification // TODO: Better in-progress notification
inProgress = Toast.makeText(this, R.string.settings_toast_encryption_changing, Toast.LENGTH_LONG); inProgress = Snackbar.make(fragment.getView(), R.string.settings_toast_encryption_changing, BaseTransientBottomBar.LENGTH_INDEFINITE);
inProgress.show(); inProgress.show();
canGoBack = false;
} else { } else {
canGoBack = true;
if (inProgress != null) if (inProgress != null)
inProgress.cancel(); inProgress.dismiss();
canGoBack = true;
} }
} }
@Override
public void onResume() {
super.onResume();
checkBackgroundTask();
}
@Override
protected void onPause() {
super.onPause();
// We don't want the task to callback to a dead activity and cause a memory leak, so null it here.
SettingsActivity.TaskFragment taskFragment = findTaskFragment();
if (taskFragment != null)
taskFragment.task.setCallback(null);
}
private void requestBackupAccess() { private void requestBackupAccess() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@ -344,10 +279,10 @@ public class SettingsActivity extends BaseActivity
EncryptionType newEncType = EncryptionType.valueOf(newEnc); EncryptionType newEncType = EncryptionType.valueOf(newEnc);
startEncryptionChangeTask(newEncType, authKey); startEncryptionChangeTask(newEncType, authKey);
} else { } else {
Toast.makeText(this, R.string.settings_toast_encryption_no_key, Toast.LENGTH_LONG).show(); Snackbar.make(fragment.getView(), R.string.settings_toast_encryption_no_key, BaseTransientBottomBar.LENGTH_SHORT).show();
} }
} else { } else {
Toast.makeText(this, R.string.settings_toast_encryption_auth_failed, Toast.LENGTH_LONG).show(); Snackbar.make(fragment.getView(), R.string.settings_toast_encryption_auth_failed, BaseTransientBottomBar.LENGTH_SHORT).show();
} }
} else if (requestCode == Constants.INTENT_SETTINGS_BACKUP_LOCATION && resultCode == RESULT_OK) { } else if (requestCode == Constants.INTENT_SETTINGS_BACKUP_LOCATION && resultCode == RESULT_OK) {
Uri treeUri = data.getData(); Uri treeUri = data.getData();
@ -573,19 +508,4 @@ public class SettingsActivity extends BaseActivity
} }
} }
} }
/** Retained instance fragment to hold a running {@link ChangeEncryptionTask} between configuration changes.*/
public static class TaskFragment extends Fragment {
ChangeEncryptionTask task;
public TaskFragment() {
super();
setRetainInstance(true);
}
public void startTask(@NonNull ChangeEncryptionTask task) {
this.task = task;
task.execute();
}
}
} }

View file

@ -28,8 +28,8 @@ import android.content.Intent;
import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Tasks.BackupTaskResult;
import org.shadowice.flocke.andotp.Tasks.EncryptedBackupTask; import org.shadowice.flocke.andotp.Tasks.EncryptedBackupTask;
import org.shadowice.flocke.andotp.Tasks.GenericBackupTask;
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;
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper; import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
@ -81,9 +81,9 @@ public class EncryptedBackupBroadcastReceiver extends BackupBroadcastReceiver {
task.execute(); task.execute();
} }
private void handleTaskResult(GenericBackupTask.BackupTaskResult result) { private void handleTaskResult(BackupTaskResult result) {
if (result.success) { if (result.success) {
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_SUCCESS, R.string.backup_receiver_title_backup_success, result.fileName); NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_SUCCESS, R.string.backup_receiver_title_backup_success, result.payload);
} else { } else {
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_FAILED, R.string.backup_receiver_title_backup_failed, result.messageId); NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_FAILED, R.string.backup_receiver_title_backup_failed, result.messageId);
} }

View file

@ -28,7 +28,7 @@ import android.content.Intent;
import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Tasks.GenericBackupTask; import org.shadowice.flocke.andotp.Tasks.BackupTaskResult;
import org.shadowice.flocke.andotp.Tasks.PlainTextBackupTask; import org.shadowice.flocke.andotp.Tasks.PlainTextBackupTask;
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;
@ -74,9 +74,9 @@ public class PlainTextBackupBroadcastReceiver extends BackupBroadcastReceiver {
task.execute(); task.execute();
} }
private void handleTaskResult(GenericBackupTask.BackupTaskResult result) { private void handleTaskResult(BackupTaskResult result) {
if (result.success) { if (result.success) {
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_SUCCESS, R.string.backup_receiver_title_backup_success, result.fileName); NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_SUCCESS, R.string.backup_receiver_title_backup_success, result.payload);
} else { } else {
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_FAILED, R.string.backup_receiver_title_backup_failed, result.messageId); NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_FAILED, R.string.backup_receiver_title_backup_failed, result.messageId);
} }

View file

@ -0,0 +1,47 @@
package org.shadowice.flocke.andotp.Tasks;
import android.content.Intent;
import android.net.Uri;
import org.shadowice.flocke.andotp.R;
public class BackupTaskResult {
public final boolean success;
public final String payload;
public final int messageId;
public boolean isPGP = false;
public Intent decryptIntent = null;
public Uri uri = null;
public final ResultType resultType;
public enum ResultType {
NONE, BACKUP, RESTORE
}
public BackupTaskResult(ResultType type, boolean success, String payload, int messageId) {
this.resultType = type;
this.success = success;
this.payload = payload;
this.messageId = messageId;
}
public BackupTaskResult(ResultType type, boolean success, String payload, int messageId, boolean isPGP, Intent decryptIntent, Uri uri) {
this.resultType = type;
this.success = success;
this.payload = payload;
this.messageId = messageId;
this.isPGP = isPGP;
this.decryptIntent = decryptIntent;
this.uri = uri;
}
public static BackupTaskResult success(ResultType type, String payload) {
return new BackupTaskResult(type, true, payload, R.string.backup_toast_export_success);
}
public static BackupTaskResult failure(ResultType type, int messageId) {
return new BackupTaskResult(type, false, null, messageId);
}
}

View file

@ -28,7 +28,7 @@ public class EncryptedRestoreTask extends GenericRestoreTask {
@Override @Override
@NonNull @NonNull
protected RestoreTaskResult doInBackground() { protected BackupTaskResult doInBackground() {
boolean success = true; boolean success = true;
String decryptedString = ""; String decryptedString = "";
@ -58,9 +58,9 @@ public class EncryptedRestoreTask extends GenericRestoreTask {
} }
if (success) { if (success) {
return RestoreTaskResult.success(decryptedString); return BackupTaskResult.success(BackupTaskResult.ResultType.RESTORE, decryptedString);
} else { } else {
return RestoreTaskResult.failure(R.string.backup_toast_import_decryption_failed); return BackupTaskResult.failure(BackupTaskResult.ResultType.RESTORE, R.string.backup_toast_import_decryption_failed);
} }
} }
} }

View file

@ -12,14 +12,14 @@ import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.Settings; import org.shadowice.flocke.andotp.Utilities.Settings;
import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper; import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper;
public abstract class GenericBackupTask extends UiBasedBackgroundTask<GenericBackupTask.BackupTaskResult> { public abstract class GenericBackupTask extends UiBasedBackgroundTask<BackupTaskResult> {
protected final Context applicationContext; protected final Context applicationContext;
protected final Settings settings; protected final Settings settings;
protected final Constants.BackupType type; protected final Constants.BackupType type;
protected Uri uri; protected Uri uri;
public GenericBackupTask(Context context, @Nullable Uri uri) { public GenericBackupTask(Context context, @Nullable Uri uri) {
super(BackupTaskResult.failure()); super(BackupTaskResult.failure(BackupTaskResult.ResultType.BACKUP, R.string.backup_toast_export_failed));
this.applicationContext = context.getApplicationContext(); this.applicationContext = context.getApplicationContext();
this.settings = new Settings(applicationContext); this.settings = new Settings(applicationContext);
@ -37,7 +37,7 @@ public abstract class GenericBackupTask extends UiBasedBackgroundTask<GenericBac
BackupHelper.BackupFile backupFile = BackupHelper.backupFile(applicationContext, settings.getBackupLocation(), type); BackupHelper.BackupFile backupFile = BackupHelper.backupFile(applicationContext, settings.getBackupLocation(), type);
if (backupFile.file == null) if (backupFile.file == null)
return new BackupTaskResult(false, backupFile.errorMessage, null); return new BackupTaskResult(BackupTaskResult.ResultType.BACKUP,false, null, backupFile.errorMessage);
uri = backupFile.file.getUri(); uri = backupFile.file.getUri();
fileName = backupFile.file.getName(); fileName = backupFile.file.getName();
@ -48,33 +48,12 @@ public abstract class GenericBackupTask extends UiBasedBackgroundTask<GenericBac
boolean success = doBackup(); boolean success = doBackup();
if (success) if (success)
return BackupTaskResult.success(fileName); return BackupTaskResult.success(BackupTaskResult.ResultType.BACKUP ,fileName);
else else
return BackupTaskResult.failure(); return BackupTaskResult.failure(BackupTaskResult.ResultType.BACKUP, R.string.backup_toast_export_failed);
} }
@NonNull @NonNull
protected abstract Constants.BackupType getBackupType(); protected abstract Constants.BackupType getBackupType();
protected abstract boolean doBackup(); protected abstract boolean doBackup();
public static class BackupTaskResult {
public final boolean success;
public final int messageId;
public final String fileName;
public BackupTaskResult(boolean success, int messageId, String fileName) {
this.success = success;
this.messageId = messageId;
this.fileName = fileName;
}
public static BackupTaskResult success(String fileName) {
return new BackupTaskResult(true, R.string.backup_toast_export_success, fileName);
}
public static BackupTaskResult failure() {
return new BackupTaskResult(false, R.string.backup_toast_export_failed, null);
}
}
} }

View file

@ -1,7 +1,6 @@
package org.shadowice.flocke.andotp.Tasks; package org.shadowice.flocke.andotp.Tasks;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -10,13 +9,13 @@ import androidx.annotation.Nullable;
import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Utilities.Settings; import org.shadowice.flocke.andotp.Utilities.Settings;
public abstract class GenericRestoreTask extends UiBasedBackgroundTask<GenericRestoreTask.RestoreTaskResult> { public abstract class GenericRestoreTask extends UiBasedBackgroundTask<BackupTaskResult> {
protected final Context applicationContext; protected final Context applicationContext;
protected final Settings settings; protected final Settings settings;
protected Uri uri; protected Uri uri;
public GenericRestoreTask(Context context, @Nullable Uri uri) { public GenericRestoreTask(Context context, @Nullable Uri uri) {
super(GenericRestoreTask.RestoreTaskResult.failure(R.string.backup_toast_import_failed)); super(BackupTaskResult.failure(BackupTaskResult.ResultType.RESTORE, R.string.backup_toast_import_failed));
this.applicationContext = context.getApplicationContext(); this.applicationContext = context.getApplicationContext();
this.settings = new Settings(applicationContext); this.settings = new Settings(applicationContext);
@ -26,38 +25,5 @@ public abstract class GenericRestoreTask extends UiBasedBackgroundTask<GenericRe
@Override @Override
@NonNull @NonNull
protected abstract RestoreTaskResult doInBackground(); protected abstract BackupTaskResult doInBackground();
public static class RestoreTaskResult {
public final boolean success;
public final String payload;
public final int messageId;
public boolean isPGP = false;
public Intent decryptIntent = null;
public Uri uri = null;
public RestoreTaskResult(boolean success, String payload, int messageId) {
this.success = success;
this.payload = payload;
this.messageId = messageId;
}
public RestoreTaskResult(boolean success, String payload, int messageId, boolean isPGP, Intent decryptIntent, Uri uri) {
this.success = success;
this.payload = payload;
this.messageId = messageId;
this.isPGP = isPGP;
this.decryptIntent = decryptIntent;
this.uri = uri;
}
public static RestoreTaskResult success(String payload) {
return new RestoreTaskResult(true, payload, 0);
}
public static RestoreTaskResult failure(int messageId) {
return new RestoreTaskResult(false, null, messageId);
}
}
} }

View file

@ -18,9 +18,9 @@ public class PGPRestoreTask extends GenericRestoreTask {
@Override @Override
@NonNull @NonNull
protected RestoreTaskResult doInBackground() { protected BackupTaskResult doInBackground() {
String data = StorageAccessHelper.loadFileString(applicationContext, uri); String data = StorageAccessHelper.loadFileString(applicationContext, uri);
return new RestoreTaskResult(true, data, 0, true, decryptIntent, uri); return new BackupTaskResult(BackupTaskResult.ResultType.RESTORE,true, data, 0, true, decryptIntent, uri);
} }
} }

View file

@ -14,8 +14,8 @@ public class PlainTextRestoreTask extends GenericRestoreTask {
@Override @Override
@NonNull @NonNull
protected RestoreTaskResult doInBackground() { protected BackupTaskResult doInBackground() {
String data = StorageAccessHelper.loadFileString(applicationContext, uri); String data = StorageAccessHelper.loadFileString(applicationContext, uri);
return RestoreTaskResult.success(data); return BackupTaskResult.success(BackupTaskResult.ResultType.RESTORE, data);
} }
} }

View file

@ -27,7 +27,6 @@ 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) {
@ -53,10 +52,7 @@ 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(() -> { mainThreadHandler.post(() -> callback.onResult(result));
callback.onResult(result);
isRunning = false;
});
this.callback = null; this.callback = null;
this.awaitedResult = null; this.awaitedResult = null;
} }
@ -64,7 +60,6 @@ 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);
} }
@ -101,15 +96,9 @@ 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

View file

@ -63,8 +63,8 @@ import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.Database.EntryList; import org.shadowice.flocke.andotp.Database.EntryList;
import org.shadowice.flocke.andotp.Dialogs.ManualEntryDialog; import org.shadowice.flocke.andotp.Dialogs.ManualEntryDialog;
import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Tasks.BackupTaskResult;
import org.shadowice.flocke.andotp.Tasks.EncryptedBackupTask; import org.shadowice.flocke.andotp.Tasks.EncryptedBackupTask;
import org.shadowice.flocke.andotp.Tasks.GenericBackupTask;
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;
@ -198,7 +198,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
} }
} }
private void handleTaskResult(GenericBackupTask.BackupTaskResult result) { private void handleTaskResult(BackupTaskResult result) {
if (result.success) { if (result.success) {
Toast.makeText(context, R.string.backup_toast_export_success, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.backup_toast_export_success, Toast.LENGTH_LONG).show();
} else { } else {
@ -270,9 +270,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
Constants.CardLayouts layout = settings.getCardLayout(); Constants.CardLayouts layout = settings.getCardLayout();
if (layout == Constants.CardLayouts.DEFAULT) { if (layout == Constants.CardLayouts.COMPACT) {
cardLayout = R.layout.component_card_default;
} else if (layout == Constants.CardLayouts.COMPACT) {
cardLayout = R.layout.component_card_compact; cardLayout = R.layout.component_card_compact;
} else if (layout == Constants.CardLayouts.FULL) { } else if (layout == Constants.CardLayouts.FULL) {
cardLayout = R.layout.component_card_full; cardLayout = R.layout.component_card_full;
@ -371,7 +369,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
private void copyHandler(final int position, final String text, final boolean dropToBackground) { private void copyHandler(final int position, final String text, final boolean dropToBackground) {
Tools.copyToClipboard(context, text); Tools.copyToClipboard(context, text);
updateLastUsedAndFrequency(position, getRealIndex(position)); updateLastUsedAndFrequency(position, getRealIndex(position));
if(context != null && dropToBackground) { if (dropToBackground) {
((MainActivity)context).moveTaskToBack(true); ((MainActivity)context).moveTaskToBack(true);
} }
} }