Merge branch '1229-setup-crash' into 'master'

Store nickname and password across screen rotations

Closes #1229

See merge request akwizgran/briar!796
This commit is contained in:
akwizgran
2018-05-14 09:57:12 +00:00
9 changed files with 123 additions and 60 deletions

View File

@@ -8,15 +8,21 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import javax.annotation.Nullable;
import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT; import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE; import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.briar.android.util.UiUtils.setError; import static org.briarproject.briar.android.util.UiUtils.setError;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class AuthorNameFragment extends SetupFragment { public class AuthorNameFragment extends SetupFragment {
private final static String TAG = AuthorNameFragment.class.getName(); private final static String TAG = AuthorNameFragment.class.getName();
@@ -30,8 +36,9 @@ public class AuthorNameFragment extends SetupFragment {
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater,
Bundle savedInstanceState) { @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
getActivity().setTitle(getString(R.string.setup_title)); getActivity().setTitle(getString(R.string.setup_title));
View v = inflater.inflate(R.layout.fragment_setup_author_name, View v = inflater.inflate(R.layout.fragment_setup_author_name,
container, false); container, false);
@@ -75,6 +82,7 @@ public class AuthorNameFragment extends SetupFragment {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
setupController.setAuthorName(authorNameInput.getText().toString()); setupController.setAuthorName(authorNameInput.getText().toString());
setupController.showPasswordFragment();
} }
} }

View File

@@ -10,7 +10,8 @@ import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener; import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
@@ -21,7 +22,8 @@ import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
@NotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault
public class DozeFragment extends SetupFragment public class DozeFragment extends SetupFragment
implements OnCheckedChangedListener { implements OnCheckedChangedListener {

View File

@@ -11,15 +11,21 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import javax.annotation.Nullable;
import static android.content.Context.INPUT_METHOD_SERVICE; import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class PasswordFragment extends SetupFragment { public class PasswordFragment extends SetupFragment {
private final static String TAG = PasswordFragment.class.getName(); private final static String TAG = PasswordFragment.class.getName();
@@ -37,8 +43,9 @@ public class PasswordFragment extends SetupFragment {
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater,
Bundle savedInstanceState) { @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
getActivity().setTitle(getString(R.string.setup_password_intro)); getActivity().setTitle(getString(R.string.setup_password_intro));
View v = inflater.inflate(R.layout.fragment_setup_password, container, View v = inflater.inflate(R.layout.fragment_setup_password, container,
false); false);
@@ -105,16 +112,17 @@ public class PasswordFragment extends SetupFragment {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (!setupController.needToShowDozeFragment()) {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
}
String password = passwordEntry.getText().toString();
IBinder token = passwordEntry.getWindowToken(); IBinder token = passwordEntry.getWindowToken();
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE); Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(token, 0); ((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
setupController.setPassword(password); setupController.setPassword(passwordEntry.getText().toString());
setupController.showDozeOrCreateAccount(); if (setupController.needToShowDozeFragment()) {
setupController.showDozeFragment();
} else {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
setupController.createAccount();
}
} }
} }

View File

@@ -4,23 +4,34 @@ import android.annotation.TargetApi;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SetupActivity extends BaseActivity public class SetupActivity extends BaseActivity
implements BaseFragmentListener { implements BaseFragmentListener {
private static final String STATE_KEY_AUTHOR_NAME = "authorName";
private static final String STATE_KEY_PASSWORD = "password";
@Inject @Inject
SetupController setupController; SetupController setupController;
@Nullable
private String authorName, password;
@Override @Override
public void onCreate(Bundle state) { public void onCreate(@Nullable Bundle state) {
super.onCreate(state); super.onCreate(state);
// fade-in after splash screen instead of default animation // fade-in after splash screen instead of default animation
overridePendingTransition(R.anim.fade_in, R.anim.fade_out); overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
@@ -28,6 +39,9 @@ public class SetupActivity extends BaseActivity
if (state == null) { if (state == null) {
showInitialFragment(AuthorNameFragment.newInstance()); showInitialFragment(AuthorNameFragment.newInstance());
} else {
authorName = state.getString(STATE_KEY_AUTHOR_NAME);
password = state.getString(STATE_KEY_PASSWORD);
} }
} }
@@ -37,16 +51,46 @@ public class SetupActivity extends BaseActivity
setupController.setSetupActivity(this); setupController.setSetupActivity(this);
} }
public void showPasswordFragment() { @Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
if (authorName != null)
state.putString(STATE_KEY_AUTHOR_NAME, authorName);
if (password != null)
state.putString(STATE_KEY_PASSWORD, password);
}
@Nullable
String getAuthorName() {
return authorName;
}
void setAuthorName(String authorName) {
this.authorName = authorName;
}
@Nullable
String getPassword() {
return password;
}
void setPassword(String password) {
this.password = password;
}
void showPasswordFragment() {
if (authorName == null) throw new IllegalStateException();
showNextFragment(PasswordFragment.newInstance()); showNextFragment(PasswordFragment.newInstance());
} }
@TargetApi(23) @TargetApi(23)
public void showDozeFragment() { void showDozeFragment() {
if (authorName == null) throw new IllegalStateException();
if (password == null) throw new IllegalStateException();
showNextFragment(DozeFragment.newInstance()); showNextFragment(DozeFragment.newInstance());
} }
public void showApp() { void showApp() {
Intent i = new Intent(this, OpenDatabaseActivity.class); Intent i = new Intent(this, OpenDatabaseActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK); i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i); startActivity(i);

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.login;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.controller.handler.ResultHandler;
@NotNullByDefault @NotNullByDefault
public interface SetupController { public interface SetupController {
@@ -17,14 +16,19 @@ public interface SetupController {
float estimatePasswordStrength(String password); float estimatePasswordStrength(String password);
/** /**
* This should be called after the author name and the password have been * This should be called after the author name has been set.
* set. It decides whether to ask for doze exception or create the account
* right away.
*/ */
void showDozeOrCreateAccount(); void showPasswordFragment();
/**
* This should be called after the author name and the password have been
* set.
*/
void showDozeFragment();
/**
* This should be called after the author name and the password have been
* set.
*/
void createAccount(); void createAccount();
void createAccount(ResultHandler<Void> resultHandler);
} }

View File

@@ -20,8 +20,6 @@ import javax.inject.Inject;
public class SetupControllerImpl extends PasswordControllerImpl public class SetupControllerImpl extends PasswordControllerImpl
implements SetupController { implements SetupController {
@Nullable
private String authorName, password;
@Nullable @Nullable
private SetupActivity setupActivity; private SetupActivity setupActivity;
@@ -48,28 +46,31 @@ public class SetupControllerImpl extends PasswordControllerImpl
@Override @Override
public void setAuthorName(String authorName) { public void setAuthorName(String authorName) {
this.authorName = authorName; if (setupActivity == null) throw new IllegalStateException();
setupActivity.setAuthorName(authorName);
}
@Override
public void setPassword(String password) {
if (setupActivity == null) throw new IllegalStateException();
setupActivity.setPassword(password);
}
@Override
public void showPasswordFragment() {
if (setupActivity == null) throw new IllegalStateException(); if (setupActivity == null) throw new IllegalStateException();
setupActivity.showPasswordFragment(); setupActivity.showPasswordFragment();
} }
@Override @Override
public void setPassword(String password) { public void showDozeFragment() {
this.password = password;
}
@Override
public void showDozeOrCreateAccount() {
if (setupActivity == null) throw new IllegalStateException(); if (setupActivity == null) throw new IllegalStateException();
if (needToShowDozeFragment()) { setupActivity.showDozeFragment();
setupActivity.showDozeFragment();
} else {
createAccount();
}
} }
@Override @Override
public void createAccount() { public void createAccount() {
if (setupActivity == null) throw new IllegalStateException();
UiResultHandler<Void> resultHandler = UiResultHandler<Void> resultHandler =
new UiResultHandler<Void>(setupActivity) { new UiResultHandler<Void>(setupActivity) {
@Override @Override
@@ -82,10 +83,13 @@ public class SetupControllerImpl extends PasswordControllerImpl
createAccount(resultHandler); createAccount(resultHandler);
} }
@Override // Package access for testing
public void createAccount(ResultHandler<Void> resultHandler) { void createAccount(ResultHandler<Void> resultHandler) {
if (authorName == null || password == null) if (setupActivity == null) throw new IllegalStateException();
throw new IllegalStateException(); String authorName = setupActivity.getAuthorName();
if (authorName == null) throw new IllegalStateException();
String password = setupActivity.getPassword();
if (password == null) throw new IllegalStateException();
cryptoExecutor.execute(() -> { cryptoExecutor.execute(() -> {
databaseConfig.setLocalAuthorName(authorName); databaseConfig.setLocalAuthorName(authorName);
SecretKey key = crypto.generateSecretKey(); SecretKey key = crypto.generateSecretKey();

View File

@@ -73,7 +73,7 @@ public class PasswordFragmentTest {
// assert controller has been called properly // assert controller has been called properly
verify(setupController, times(1)).setPassword(safePass); verify(setupController, times(1)).setPassword(safePass);
verify(setupController, times(1)).showDozeOrCreateAccount(); verify(setupController, times(1)).createAccount();
} }
@Test @Test

View File

@@ -15,22 +15,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = 21, application = TestBriarApplication.class, @Config(sdk = 21, application = TestBriarApplication.class,

View File

@@ -47,10 +47,17 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
} }
@Test @Test
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testCreateAccount() { public void testCreateAccount() {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Setting the author name shows the password fragment // Set the author name and password
oneOf(setupActivity).showPasswordFragment(); oneOf(setupActivity).setAuthorName(authorName);
oneOf(setupActivity).setPassword(password);
// Get the author name and password
oneOf(setupActivity).getAuthorName();
will(returnValue(authorName));
oneOf(setupActivity).getPassword();
will(returnValue(password));
// Generate a database key // Generate a database key
oneOf(crypto).generateSecretKey(); oneOf(crypto).generateSecretKey();
will(returnValue(key)); will(returnValue(key));