Store the running ChangeEncryptionTask in a fragment
This way it survives when the calling activity goes into the background
This commit is contained in:
parent
d8a1e03806
commit
d95dac1784
3 changed files with 125 additions and 18 deletions
|
@ -22,6 +22,7 @@
|
|||
|
||||
package org.shadowice.flocke.andotp.Activities;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.backup.BackupManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
|
@ -38,10 +39,12 @@ import android.preference.PreferenceFragment;
|
|||
import android.preference.PreferenceManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -68,6 +71,8 @@ import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType;
|
|||
public class SettingsActivity extends BaseActivity
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener, EncryptionHelper.EncryptionChangeCallback {
|
||||
|
||||
private static final String TAG_TASK_FRAGMENT = "SettingsActivity.TaskFragmentTag";
|
||||
|
||||
private SettingsFragment fragment;
|
||||
private SharedPreferences prefs;
|
||||
|
||||
|
@ -75,6 +80,7 @@ public class SettingsActivity extends BaseActivity
|
|||
private boolean encryptionChanged = false;
|
||||
|
||||
private Toast inProgress = null;
|
||||
private boolean canGoBack = true;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -115,6 +121,14 @@ public class SettingsActivity extends BaseActivity
|
|||
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
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
@ -142,8 +156,10 @@ public class SettingsActivity extends BaseActivity
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
finishWithResult();
|
||||
super.onBackPressed();
|
||||
if (canGoBack) {
|
||||
finishWithResult();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||
|
@ -211,7 +227,7 @@ public class SettingsActivity extends BaseActivity
|
|||
}
|
||||
|
||||
private void handleTaskResult(ChangeEncryptionTask.Result result) {
|
||||
inProgress.cancel();
|
||||
setupUiForTaskState(false);
|
||||
|
||||
switch (result.result) {
|
||||
case SUCCESS:
|
||||
|
@ -225,17 +241,89 @@ public class SettingsActivity extends BaseActivity
|
|||
Toast.makeText(this, R.string.settings_toast_encryption_change_failed, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove the task fragment after the task is finished
|
||||
TaskFragment taskFragment = findTaskFragment();
|
||||
if (taskFragment != null) {
|
||||
getFragmentManager().beginTransaction()
|
||||
.remove(taskFragment)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private void tryEncryptionChange(EncryptionType newEnc, byte[] newKey) {
|
||||
ChangeEncryptionTask task = new ChangeEncryptionTask(this, encryptionKey, newEnc, newKey);
|
||||
task.setCallback(this::handleTaskResult);
|
||||
@Nullable
|
||||
private SettingsActivity.TaskFragment findTaskFragment() {
|
||||
return (SettingsActivity.TaskFragment) getFragmentManager().findFragmentByTag(TAG_TASK_FRAGMENT);
|
||||
}
|
||||
|
||||
// TODO: Better in-progress notification
|
||||
inProgress = Toast.makeText(this, R.string.settings_toast_encryption_changing, Toast.LENGTH_LONG);
|
||||
inProgress.show();
|
||||
private void startEncryptionChangeTask(EncryptionType newEnc, byte[] newKey) {
|
||||
SettingsActivity.TaskFragment taskFragment = findTaskFragment();
|
||||
// Don't start a task if we already have an active task running.
|
||||
if (taskFragment == null || taskFragment.task.isCanceled()) {
|
||||
ChangeEncryptionTask task = new ChangeEncryptionTask(this, encryptionKey, newEnc, newKey);
|
||||
task.setCallback(this::handleTaskResult);
|
||||
|
||||
task.execute();
|
||||
if (taskFragment == null) {
|
||||
taskFragment = new SettingsActivity.TaskFragment();
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.add(taskFragment, TAG_TASK_FRAGMENT)
|
||||
.commit();
|
||||
}
|
||||
|
||||
taskFragment.startTask(task);
|
||||
setupUiForTaskState(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBackgroundTask() {
|
||||
SettingsActivity.TaskFragment taskFragment = findTaskFragment();
|
||||
|
||||
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) {
|
||||
canGoBack = false;
|
||||
|
||||
// TODO: Better in-progress notification
|
||||
inProgress = Toast.makeText(this, R.string.settings_toast_encryption_changing, Toast.LENGTH_LONG);
|
||||
inProgress.show();
|
||||
} else {
|
||||
canGoBack = true;
|
||||
|
||||
if (inProgress != null)
|
||||
inProgress.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@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() {
|
||||
|
@ -262,7 +350,7 @@ public class SettingsActivity extends BaseActivity
|
|||
|
||||
if (authKey != null && authKey.length > 0 && newEnc != null && !newEnc.isEmpty()) {
|
||||
EncryptionType newEncType = EncryptionType.valueOf(newEnc);
|
||||
tryEncryptionChange(newEncType, authKey);
|
||||
startEncryptionChangeTask(newEncType, authKey);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.settings_toast_encryption_no_key, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
@ -317,7 +405,7 @@ public class SettingsActivity extends BaseActivity
|
|||
if (encryptionType == EncryptionType.PASSWORD)
|
||||
((SettingsActivity) getActivity()).tryEncryptionChangeWithAuth(encryptionType);
|
||||
else if (encryptionType == EncryptionType.KEYSTORE)
|
||||
((SettingsActivity) getActivity()).tryEncryptionChange(encryptionType, null);
|
||||
((SettingsActivity) getActivity()).startEncryptionChangeTask(encryptionType, null);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
|
||||
})
|
||||
|
@ -493,4 +581,19 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,14 +40,14 @@ public abstract class UiBasedBackgroundTask<Result> {
|
|||
public void setCallback(@Nullable UiCallback<Result> callback) {
|
||||
synchronized (callbackLock) {
|
||||
// Don't bother doing anything if the task was canceled.
|
||||
if (isCanceled()) {
|
||||
if (isCanceled())
|
||||
return;
|
||||
}
|
||||
|
||||
this.callback = callback;
|
||||
|
||||
// If we have an awaited result and are setting a new callback, publish the result immediately.
|
||||
if (awaitedResult != null && callback != null) {
|
||||
if (awaitedResult != null && callback != null)
|
||||
emitResultOnMainThread(callback, awaitedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,9 +73,9 @@ public abstract class UiBasedBackgroundTask<Result> {
|
|||
|
||||
synchronized (callbackLock) {
|
||||
// Don't bother issuing callback or storing result if this task is canceled.
|
||||
if (isCanceled()) {
|
||||
if (isCanceled())
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
emitResultOnMainThread(callback, result);
|
||||
} else {
|
||||
|
|
|
@ -90,6 +90,10 @@ public class EncryptionHelper {
|
|||
}
|
||||
|
||||
if (DatabaseHelper.saveDatabase(context, entries, newEncryptionKey)) {
|
||||
// Persist new encryption here already so everything is fully setup when the task finishes
|
||||
Settings settings = new Settings(context);
|
||||
settings.setEncryption(newType);
|
||||
|
||||
if (changeCallback != null)
|
||||
changeCallback.onSuccessfulEncryptionChange(newType, newEncryptionKey);
|
||||
|
||||
|
|
Loading…
Reference in a new issue