From e37ee7ee04fdb15b28e9a7a463bbf8671e066b92 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 9 May 2019 15:16:53 -0300 Subject: [PATCH] [android] Use LiveEvent to communicate password validation and account deletion --- .../briar/android/activity/BaseActivity.java | 6 +++++ .../briar/android/login/PasswordFragment.java | 25 ++++++++----------- .../briar/android/login/StartupActivity.java | 17 +++++++------ .../briar/android/login/StartupViewModel.java | 18 +++++++------ 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java index 3cc44414f..49e86c469 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java @@ -172,6 +172,12 @@ public abstract class BaseActivity extends AppCompatActivity .commit(); } + protected boolean isFragmentAdded(String fragmentTag) { + FragmentManager fm = getSupportFragmentManager(); + Fragment f = fm.findFragmentByTag(fragmentTag); + return f != null && f.isAdded(); + } + private boolean showScreenFilterWarning() { // If the dialog is already visible, filter the tap ScreenFilterDialogFragment f = findDialogFragment(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java index d83421ad2..60828d058 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java @@ -35,7 +35,7 @@ import static org.briarproject.briar.android.util.UiUtils.showSoftKeyboard; @ParametersNotNullByDefault public class PasswordFragment extends BaseFragment implements TextWatcher { - private final static String TAG = PasswordFragment.class.getName(); + final static String TAG = PasswordFragment.class.getName(); @Inject ViewModelProvider.Factory viewModelFactory; @@ -60,7 +60,9 @@ public class PasswordFragment extends BaseFragment implements TextWatcher { viewModel = ViewModelProviders.of(requireActivity(), viewModelFactory) .get(StartupViewModel.class); - viewModel.getPasswordValidated().observe(this, this::onPasswordValidated); + viewModel.getPasswordValidated().observeEvent(this, valid -> { + if (!valid) onPasswordInvalid(); + }); signInButton = v.findViewById(R.id.btn_sign_in); signInButton.setOnClickListener(view -> onSignInButtonClicked()); @@ -103,19 +105,14 @@ public class PasswordFragment extends BaseFragment implements TextWatcher { viewModel.validatePassword(password.getText().toString()); } - private void onPasswordValidated(@Nullable Boolean valid) { - if (valid != null && !valid) { - setError(input, getString(R.string.try_again), true); - signInButton.setVisibility(VISIBLE); - progress.setVisibility(INVISIBLE); - password.setText(null); + private void onPasswordInvalid() { + setError(input, getString(R.string.try_again), true); + signInButton.setVisibility(VISIBLE); + progress.setVisibility(INVISIBLE); + password.setText(null); - // show the keyboard again - showSoftKeyboard(password); - - // reset validation state for configuration changes - viewModel.getPasswordValidated().setValue(null); - } + // show the keyboard again + showSoftKeyboard(password); } public void onForgottenPasswordClick() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java index bf76d82ba..e476a9f89 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/StartupActivity.java @@ -5,8 +5,6 @@ import android.arch.lifecycle.ViewModelProviders; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; @@ -63,8 +61,8 @@ public class StartupActivity extends BaseActivity implements onAccountDeleted(); return; } - viewModel.getAccountDeleted().observe(this, deleted -> { - if (deleted != null && deleted) onAccountDeleted(); + viewModel.getAccountDeleted().observeEvent(this, deleted -> { + if (deleted) onAccountDeleted(); }); viewModel.getState().observe(this, this::onStateChanged); } @@ -85,7 +83,12 @@ public class StartupActivity extends BaseActivity implements private void onStateChanged(State state) { if (state == SIGNED_OUT) { - showInitialFragment(new PasswordFragment()); + // Configuration changes such as screen rotation + // can cause this to get called again. + // Don't replace the fragment in that case to not lose view state. + if (!isFragmentAdded(PasswordFragment.TAG)) { + showInitialFragment(new PasswordFragment()); + } } else if (state == SIGNED_IN) { startService(new Intent(this, BriarService.class)); } else if (state == STARTING) { @@ -93,9 +96,7 @@ public class StartupActivity extends BaseActivity implements // This can happen because several LifecycleManager states are // mapped to STARTING, so this can get called several times // as the app's lifecycle advances. - FragmentManager fm = getSupportFragmentManager(); - Fragment f = fm.findFragmentByTag(OpenDatabaseFragment.TAG); - if (f == null || !f.isVisible()) { + if (!isFragmentAdded(OpenDatabaseFragment.TAG)) { showNextFragment(new OpenDatabaseFragment()); } } else if (state == STARTED) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/StartupViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/login/StartupViewModel.java index a152c863b..d87f312fc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/StartupViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/StartupViewModel.java @@ -15,6 +15,8 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.android.viewmodel.LiveEvent; +import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.api.android.AndroidNotificationManager; import java.util.concurrent.Executor; @@ -43,10 +45,10 @@ public class StartupViewModel extends AndroidViewModel @IoExecutor private final Executor ioExecutor; - private final MutableLiveData passwordValidated = - new MutableLiveData<>(); - private final MutableLiveData accountDeleted = - new MutableLiveData<>(); + private final MutableLiveEvent passwordValidated = + new MutableLiveEvent<>(); + private final MutableLiveEvent accountDeleted = + new MutableLiveEvent<>(); private final MutableLiveData state = new MutableLiveData<>(); @Inject @@ -103,16 +105,16 @@ public class StartupViewModel extends AndroidViewModel void validatePassword(String password) { ioExecutor.execute(() -> { boolean signedIn = accountManager.signIn(password); - passwordValidated.postValue(signedIn); + passwordValidated.postEvent(signedIn); if (signedIn) state.postValue(SIGNED_IN); }); } - MutableLiveData getPasswordValidated() { + LiveEvent getPasswordValidated() { return passwordValidated; } - LiveData getAccountDeleted() { + LiveEvent getAccountDeleted() { return accountDeleted; } @@ -123,7 +125,7 @@ public class StartupViewModel extends AndroidViewModel @UiThread void deleteAccount() { accountManager.deleteAccount(); - accountDeleted.setValue(true); + accountDeleted.setEvent(true); } }