Prevent a crash on rotation while a ProgressDialogTask is still running

This patch ensures ProgressDialogs are dismissed when the ON_PAUSE event is fired.
This commit is contained in:
Alexander Bakker 2020-08-16 14:56:11 +02:00
parent 7d38bc9b71
commit bb2716f640
7 changed files with 86 additions and 8 deletions

View file

@ -0,0 +1,39 @@
package com.beemdevelopment.aegis.helpers;
import android.content.Context;
import android.content.ContextWrapper;
import androidx.activity.ComponentActivity;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import javax.annotation.Nullable;
/**
* ContextHelper contains some disgusting hacks to obtain the Activity/Lifecycle from a Context.
*/
public class ContextHelper {
private ContextHelper() {
}
// source: https://github.com/androidx/androidx/blob/e32e1da51a0c7448c74861c667fa76738a415a89/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java#L425-L435
@Nullable
public static ComponentActivity getActivity(@NonNull Context context) {
while (context instanceof ContextWrapper) {
if (context instanceof ComponentActivity) {
return (ComponentActivity) context;
}
context = ((ContextWrapper) context).getBaseContext();
}
return null;
}
@Nullable
public static Lifecycle getLifecycle(@NonNull Context context) {
ComponentActivity activity = getActivity(context);
return activity == null ? null : activity.getLifecycle();
}
}

View file

@ -3,6 +3,7 @@ package com.beemdevelopment.aegis.importers;
import android.content.Context; import android.content.Context;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Lifecycle;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.crypto.CryptParameters; import com.beemdevelopment.aegis.crypto.CryptParameters;
@ -10,6 +11,7 @@ import com.beemdevelopment.aegis.crypto.CryptResult;
import com.beemdevelopment.aegis.crypto.CryptoUtils; import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.encoding.Base32; import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.encoding.EncodingException; import com.beemdevelopment.aegis.encoding.EncodingException;
import com.beemdevelopment.aegis.helpers.ContextHelper;
import com.beemdevelopment.aegis.otp.HotpInfo; import com.beemdevelopment.aegis.otp.HotpInfo;
import com.beemdevelopment.aegis.otp.OtpInfo; import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.otp.OtpInfoException; import com.beemdevelopment.aegis.otp.OtpInfoException;
@ -148,7 +150,8 @@ public class AndOtpImporter extends DatabaseImporter {
listener.onError(e); listener.onError(e);
} }
}); });
task.execute(params); Lifecycle lifecycle = ContextHelper.getLifecycle(context);
task.execute(lifecycle, params);
} }
} }

View file

@ -154,7 +154,8 @@ public class AuthActivity extends AegisActivity {
char[] password = EditTextHelper.getEditTextChars(_textPassword); char[] password = EditTextHelper.getEditTextChars(_textPassword);
List<PasswordSlot> slots = _slots.findAll(PasswordSlot.class); List<PasswordSlot> slots = _slots.findAll(PasswordSlot.class);
PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password); PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password);
new PasswordSlotDecryptTask(AuthActivity.this, new PasswordDerivationListener()).execute(params); PasswordSlotDecryptTask task = new PasswordSlotDecryptTask(AuthActivity.this, new PasswordDerivationListener());
task.execute(getLifecycle(), params);
}); });
biometricsButton.setOnClickListener(v -> { biometricsButton.setOnClickListener(v -> {

View file

@ -23,6 +23,7 @@ import android.widget.NumberPicker;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.activity.ComponentActivity;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@ -94,7 +95,7 @@ public class Dialogs {
.create()); .create());
} }
public static void showSetPasswordDialog(Activity activity, Dialogs.SlotListener listener) { public static void showSetPasswordDialog(ComponentActivity activity, Dialogs.SlotListener listener) {
Zxcvbn zxcvbn = new Zxcvbn(); Zxcvbn zxcvbn = new Zxcvbn();
View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null); View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null);
EditText textPassword = view.findViewById(R.id.text_password); EditText textPassword = view.findViewById(R.id.text_password);
@ -150,7 +151,8 @@ public class Dialogs {
listener.onSlotResult(slot, cipher); listener.onSlotResult(slot, cipher);
dialog.dismiss(); dialog.dismiss();
}); });
task.execute(new KeyDerivationTask.Params(slot, password)); KeyDerivationTask.Params params = new KeyDerivationTask.Params(slot, password);
task.execute(activity.getLifecycle(), params);
}); });
}); });

View file

@ -37,6 +37,7 @@ import com.beemdevelopment.aegis.services.NotificationService;
import com.beemdevelopment.aegis.ui.models.ImportEntry; import com.beemdevelopment.aegis.ui.models.ImportEntry;
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference; import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask; import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask;
import com.beemdevelopment.aegis.ui.tasks.ProgressDialogTask;
import com.beemdevelopment.aegis.util.UUIDMap; import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultBackupManager; import com.beemdevelopment.aegis.vault.VaultBackupManager;
import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.vault.VaultEntry;
@ -384,7 +385,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat {
if (isDigitsOnly(new String(password))) { if (isDigitsOnly(new String(password))) {
List<PasswordSlot> slots = _vault.getCredentials().getSlots().findAll(PasswordSlot.class); List<PasswordSlot> slots = _vault.getCredentials().getSlots().findAll(PasswordSlot.class);
PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password); PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password);
new PasswordSlotDecryptTask(getActivity(), new PasswordConfirmationListener()).execute(params); PasswordSlotDecryptTask task = new PasswordSlotDecryptTask(getActivity(), new PasswordConfirmationListener());
task.execute(getLifecycle(), params);
} else { } else {
setPinKeyboardPreference(false); setPinKeyboardPreference(false);
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity()) Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())

View file

@ -125,7 +125,8 @@ public class SecuritySetupSlide extends SlideFragment {
private void deriveKey() { private void deriveKey() {
PasswordSlot slot = new PasswordSlot(); PasswordSlot slot = new PasswordSlot();
KeyDerivationTask.Params params = new KeyDerivationTask.Params(slot, EditTextHelper.getEditTextChars(_textPassword)); KeyDerivationTask.Params params = new KeyDerivationTask.Params(slot, EditTextHelper.getEditTextChars(_textPassword));
new KeyDerivationTask(getContext(), new PasswordDerivationListener()).execute(params); KeyDerivationTask task = new KeyDerivationTask(getContext(), new PasswordDerivationListener());
task.execute(getLifecycle(), params);
} }
@Override @Override

View file

@ -1,13 +1,18 @@
package com.beemdevelopment.aegis.ui.tasks; package com.beemdevelopment.aegis.ui.tasks;
import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Process; import android.os.Process;
import com.beemdevelopment.aegis.ui.Dialogs;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import com.beemdevelopment.aegis.ui.Dialogs;
public abstract class ProgressDialogTask<Params, Result> extends AsyncTask<Params, String, Result> { public abstract class ProgressDialogTask<Params, Result> extends AsyncTask<Params, String, Result> {
private ProgressDialog _dialog; private ProgressDialog _dialog;
@ -47,4 +52,29 @@ public abstract class ProgressDialogTask<Params, Result> extends AsyncTask<Param
protected final ProgressDialog getDialog() { protected final ProgressDialog getDialog() {
return _dialog; return _dialog;
} }
@SafeVarargs
public final void execute(@Nullable Lifecycle lifecycle, Params... params) {
if (lifecycle != null) {
LifecycleObserver observer = new Observer(getDialog());
lifecycle.addObserver(observer);
}
execute(params);
}
private static class Observer implements LifecycleObserver {
private Dialog _dialog;
public Observer(Dialog dialog) {
_dialog = dialog;
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause() {
if (_dialog != null && _dialog.isShowing()) {
_dialog.dismiss();
_dialog = null;
}
}
}
} }