From 8a811717399fa7c6ffd9c53a840a7b80f9c2c420 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Tue, 26 Sep 2017 10:04:48 -0300
Subject: [PATCH] Setup Wizard that asks for Doze Mode exception

Keep checking if we are whitelisted and request it if not
---
 briar-android/build.gradle                    |   3 +
 briar-android/src/main/AndroidManifest.xml    |   1 +
 .../android/activity/ActivityComponent.java   |   7 +
 .../briar/android/activity/BaseActivity.java  |  18 ++
 .../briar/android/activity/BriarActivity.java |  17 --
 .../briar/android/activity/RequestCodes.java  |  16 +-
 .../android/login/AuthorNameFragment.java     |  84 ++++++++++
 .../android/login/ChangePasswordActivity.java |   4 +-
 .../briar/android/login/DozeFragment.java     |  95 +++++++++++
 .../android/login/PasswordController.java     |   3 +
 .../android/login/PasswordControllerImpl.java |  11 +-
 .../briar/android/login/PasswordFragment.java | 117 +++++++++++++
 .../briar/android/login/SetupActivity.java    | 137 +++-------------
 .../briar/android/login/SetupController.java  |  20 ++-
 .../android/login/SetupControllerImpl.java    |  65 +++++++-
 .../briar/android/login/SetupFragment.java    |  66 ++++++++
 .../android/navdrawer/NavDrawerActivity.java  |  30 +++-
 .../briar/android/util/UiUtils.java           |  47 +++++-
 .../res/layout/fragment_setup_author_name.xml |  60 +++++++
 .../main/res/layout/fragment_setup_doze.xml   |  54 ++++++
 ..._setup.xml => fragment_setup_password.xml} |  92 +++++------
 .../src/main/res/menu/help_action.xml         |  12 ++
 .../src/main/res/values-bg/strings.xml        |   2 +-
 .../src/main/res/values-ca/strings.xml        |   2 +-
 .../src/main/res/values-de/strings.xml        |   2 +-
 .../src/main/res/values-es/strings.xml        |   2 +-
 .../src/main/res/values-eu/strings.xml        |   2 +-
 .../src/main/res/values-fi/strings.xml        |   2 +-
 .../src/main/res/values-fr/strings.xml        |   2 +-
 .../src/main/res/values-gl/strings.xml        |   2 +-
 .../src/main/res/values-hi/strings.xml        |   2 +-
 .../src/main/res/values-it/strings.xml        |   2 +-
 .../src/main/res/values-nb/strings.xml        |   2 +-
 .../src/main/res/values-oc/strings.xml        |   2 +-
 .../src/main/res/values-pt-rBR/strings.xml    |   2 +-
 .../src/main/res/values-ru/strings.xml        |   2 +-
 .../src/main/res/values-sq/strings.xml        |   2 +-
 .../src/main/res/values-sr/strings.xml        |   2 +-
 .../src/main/res/values-tr/strings.xml        |   2 +-
 .../src/main/res/values-zh-rCN/strings.xml    |   2 +-
 briar-android/src/main/res/values/strings.xml |  12 +-
 .../login/ChangePasswordActivityTest.java     |  63 +++----
 .../android/login/SetupActivityTest.java      | 155 +++++++-----------
 .../login/TestChangePasswordActivity.java     |   7 -
 build.gradle                                  |   3 +
 45 files changed, 875 insertions(+), 360 deletions(-)
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java
 create mode 100644 briar-android/src/main/res/layout/fragment_setup_author_name.xml
 create mode 100644 briar-android/src/main/res/layout/fragment_setup_doze.xml
 rename briar-android/src/main/res/layout/{activity_setup.xml => fragment_setup_password.xml} (54%)
 create mode 100644 briar-android/src/main/res/menu/help_action.xml

diff --git a/briar-android/build.gradle b/briar-android/build.gradle
index d31f16b1f8..fac88cf8ce 100644
--- a/briar-android/build.gradle
+++ b/briar-android/build.gradle
@@ -18,6 +18,7 @@ dependencies {
 	}
 	compile "com.android.support:cardview-v7:$supportVersion"
 	compile "com.android.support:support-annotations:$supportVersion"
+	compile 'com.android.support.constraint:constraint-layout:1.0.2'
 	compile('ch.acra:acra:4.8.5') {
 		exclude module: 'support-v4'
 		exclude module: 'support-annotations'
@@ -55,6 +56,8 @@ dependencyVerification {
 			'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
 			'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
 			'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
+			'com.android.support.constraint:constraint-layout:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085',
+			'com.android.support.constraint:constraint-layout-solver:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d',
 	]
 }
 
diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml
index c43e5aa27c..a2fed381f9 100644
--- a/briar-android/src/main/AndroidManifest.xml
+++ b/briar-android/src/main/AndroidManifest.xml
@@ -15,6 +15,7 @@
 	<uses-permission android:name="android.permission.READ_LOGS"/>
 	<uses-permission android:name="android.permission.VIBRATE" />
 	<uses-permission android:name="android.permission.WAKE_LOCK" />
+	<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
 
 	<application
 		android:name="org.briarproject.briar.android.BriarApplicationImpl"
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
index 0e8d460062..f02774ae51 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
@@ -27,8 +27,11 @@ import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
 import org.briarproject.briar.android.keyagreement.IntroFragment;
 import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
 import org.briarproject.briar.android.keyagreement.ShowQrCodeFragment;
+import org.briarproject.briar.android.login.AuthorNameFragment;
 import org.briarproject.briar.android.login.ChangePasswordActivity;
+import org.briarproject.briar.android.login.DozeFragment;
 import org.briarproject.briar.android.login.PasswordActivity;
+import org.briarproject.briar.android.login.PasswordFragment;
 import org.briarproject.briar.android.login.SetupActivity;
 import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
 import org.briarproject.briar.android.panic.PanicPreferencesActivity;
@@ -148,6 +151,10 @@ public interface ActivityComponent {
 	void inject(RssFeedManageActivity activity);
 
 	// Fragments
+	void inject(AuthorNameFragment fragment);
+	void inject(PasswordFragment fragment);
+	void inject(DozeFragment fragment);
+
 	void inject(ContactListFragment fragment);
 
 	void inject(CreateGroupFragment fragment);
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 e11a5bb379..628c9e646f 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
@@ -12,11 +12,13 @@ import android.view.ViewGroup.LayoutParams;
 import android.view.inputmethod.InputMethodManager;
 
 import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.briar.R;
 import org.briarproject.briar.android.AndroidComponent;
 import org.briarproject.briar.android.BriarApplication;
 import org.briarproject.briar.android.DestroyableContext;
 import org.briarproject.briar.android.controller.ActivityLifecycleController;
 import org.briarproject.briar.android.forum.ForumModule;
+import org.briarproject.briar.android.fragment.BaseFragment;
 import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
 import org.briarproject.briar.android.widget.TapSafeFrameLayout;
 import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
@@ -113,6 +115,22 @@ public abstract class BaseActivity extends AppCompatActivity
 		}
 	}
 
+	protected void showInitialFragment(BaseFragment f) {
+		getSupportFragmentManager().beginTransaction()
+				.replace(R.id.fragmentContainer, f, f.getUniqueTag())
+				.commit();
+	}
+
+	public void showNextFragment(BaseFragment f) {
+		getSupportFragmentManager().beginTransaction()
+				.setCustomAnimations(R.anim.step_next_in,
+						R.anim.step_previous_out, R.anim.step_previous_in,
+						R.anim.step_next_out)
+				.replace(R.id.fragmentContainer, f, f.getUniqueTag())
+				.addToBackStack(f.getUniqueTag())
+				.commit();
+	}
+
 	private void showScreenFilterWarning() {
 		if (dialogFrag != null && dialogFrag.isVisible()) return;
 		Set<String> apps = screenFilterMonitor.getApps();
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
index e148db65b7..33ed5bfc3a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
@@ -14,7 +14,6 @@ import org.briarproject.briar.R;
 import org.briarproject.briar.android.controller.BriarController;
 import org.briarproject.briar.android.controller.DbController;
 import org.briarproject.briar.android.controller.handler.ResultHandler;
-import org.briarproject.briar.android.fragment.BaseFragment;
 import org.briarproject.briar.android.login.PasswordActivity;
 import org.briarproject.briar.android.panic.ExitActivity;
 
@@ -64,22 +63,6 @@ public abstract class BriarActivity extends BaseActivity {
 		}
 	}
 
-	protected void showInitialFragment(BaseFragment f) {
-		getSupportFragmentManager().beginTransaction()
-				.replace(R.id.fragmentContainer, f, f.getUniqueTag())
-				.commit();
-	}
-
-	public void showNextFragment(BaseFragment f) {
-		getSupportFragmentManager().beginTransaction()
-				.setCustomAnimations(R.anim.step_next_in,
-						R.anim.step_previous_out, R.anim.step_previous_in,
-						R.anim.step_next_out)
-				.replace(R.id.fragmentContainer, f, f.getUniqueTag())
-				.addToBackStack(f.getUniqueTag())
-				.commit();
-	}
-
 	public void setSceneTransitionAnimation() {
 		if (Build.VERSION.SDK_INT < 21) return;
 		Transition slide = new Slide(Gravity.RIGHT);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java
index 131b2921b8..fe771c430c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java
@@ -3,13 +3,13 @@ package org.briarproject.briar.android.activity;
 public interface RequestCodes {
 
 	int REQUEST_PASSWORD = 1;
-	int REQUEST_BLUETOOTH = 2;
-	int REQUEST_INTRODUCTION = 3;
-	int REQUEST_GROUP_INVITE = 4;
-	int REQUEST_SHARE_FORUM = 5;
-	int REQUEST_WRITE_BLOG_POST = 6;
-	int REQUEST_SHARE_BLOG = 7;
-	int REQUEST_RINGTONE = 8;
-	int REQUEST_PERMISSION_CAMERA = 9;
+	int REQUEST_INTRODUCTION = 2;
+	int REQUEST_GROUP_INVITE = 3;
+	int REQUEST_SHARE_FORUM = 4;
+	int REQUEST_WRITE_BLOG_POST = 5;
+	int REQUEST_SHARE_BLOG = 6;
+	int REQUEST_RINGTONE = 7;
+	int REQUEST_PERMISSION_CAMERA = 8;
+	int REQUEST_DOZE_WHITELISTING = 9;
 
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java
new file mode 100644
index 0000000000..ab7cd4cd86
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java
@@ -0,0 +1,84 @@
+package org.briarproject.briar.android.login;
+
+import android.os.Bundle;
+import android.support.design.widget.TextInputEditText;
+import android.support.design.widget.TextInputLayout;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import org.briarproject.bramble.util.StringUtils;
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+
+import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
+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.briar.android.util.UiUtils.setError;
+
+public class AuthorNameFragment extends SetupFragment {
+
+	private final static String TAG = AuthorNameFragment.class.getName();
+
+	private TextInputLayout authorNameWrapper;
+	private TextInputEditText authorNameInput;
+	private Button nextButton;
+
+	public static AuthorNameFragment newInstance() {
+		return new AuthorNameFragment();
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		getActivity().setTitle(getString(R.string.setup_title));
+		View v =
+				inflater.inflate(R.layout.fragment_setup_author_name, container,
+						false);
+		authorNameWrapper =
+				(TextInputLayout) v.findViewById(R.id.nickname_entry_wrapper);
+		authorNameInput =
+				(TextInputEditText) v.findViewById(R.id.nickname_entry);
+		nextButton = (Button) v.findViewById(R.id.next);
+
+		authorNameInput.addTextChangedListener(this);
+
+		nextButton.setOnClickListener(this);
+
+		return v;
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	protected String getHelpText() {
+		return getString(R.string.setup_name_explanation);
+	}
+
+	@Override
+	public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
+		int authorNameLength = StringUtils.toUtf8(authorName.toString()).length;
+		boolean error = authorNameLength > MAX_AUTHOR_NAME_LENGTH;
+		setError(authorNameWrapper, getString(R.string.name_too_long), error);
+		boolean enabled = authorNameLength > 0 && !error;
+		authorNameInput
+				.setImeOptions(enabled ? IME_ACTION_NEXT : IME_ACTION_NONE);
+		authorNameInput.setOnEditorActionListener(enabled ? this : null);
+		nextButton.setEnabled(enabled);
+	}
+
+	@Override
+	public void onClick(View view) {
+		setupController.setAuthorName(authorNameInput.getText().toString());
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/ChangePasswordActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/ChangePasswordActivity.java
index cea17a2ebb..cc460deb7e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/ChangePasswordActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/ChangePasswordActivity.java
@@ -32,8 +32,6 @@ public class ChangePasswordActivity extends BaseActivity
 
 	@Inject
 	protected PasswordController passwordController;
-	@Inject
-	protected SetupController setupController;
 
 	private TextInputLayout currentPasswordEntryWrapper;
 	private TextInputLayout newPasswordEntryWrapper;
@@ -105,7 +103,7 @@ public class ChangePasswordActivity extends BaseActivity
 		String secondPassword = newPasswordConfirmation.getText().toString();
 		boolean passwordsMatch = firstPassword.equals(secondPassword);
 		float strength =
-				setupController.estimatePasswordStrength(firstPassword);
+				passwordController.estimatePasswordStrength(firstPassword);
 		strengthMeter.setStrength(strength);
 		UiUtils.setError(newPasswordEntryWrapper,
 				getString(R.string.password_too_weak),
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java
new file mode 100644
index 0000000000..328dac981d
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java
@@ -0,0 +1,95 @@
+package org.briarproject.briar.android.login;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.util.UiUtils;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
+import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
+
+@TargetApi(23)
+public class DozeFragment extends SetupFragment {
+
+	private final static String TAG = DozeFragment.class.getName();
+
+	private Button dozeButton;
+	private ProgressBar progressBar;
+
+	public static DozeFragment newInstance() {
+		return new DozeFragment();
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		getActivity().setTitle(getString(R.string.setup_doze_title));
+		View v =
+				inflater.inflate(R.layout.fragment_setup_doze, container,
+						false);
+		dozeButton = (Button) v.findViewById(R.id.dozeButton);
+		progressBar = (ProgressBar) v.findViewById(R.id.progress);
+
+		dozeButton.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View view) {
+				askForDozeWhitelisting();
+			}
+		});
+
+		return v;
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	protected String getHelpText() {
+		return getString(R.string.setup_doze_explanation);
+	}
+
+	@Override
+	public void onActivityResult(int request, int result, Intent data) {
+		super.onActivityResult(request, result, data);
+		if (request == REQUEST_DOZE_WHITELISTING) {
+			if (!setupController.needsDozeWhitelisting()) {
+				dozeButton.setEnabled(false);
+				onClick(dozeButton);
+			} else {
+				showOnboardingDialog(getContext(), getHelpText());
+			}
+		}
+	}
+
+	@SuppressLint("BatteryLife")
+	private void askForDozeWhitelisting() {
+		Intent i = UiUtils.getDozeWhitelistingIntent(getContext());
+		startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
+	}
+
+	@Override
+	public void onClick(View view) {
+		dozeButton.setVisibility(INVISIBLE);
+		progressBar.setVisibility(VISIBLE);
+		setupController.createAccount();
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java
index 209b5ac4a7..ec3cb7ed7f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java
@@ -7,9 +7,12 @@ import org.briarproject.briar.android.controller.handler.ResultHandler;
 @NotNullByDefault
 public interface PasswordController extends ConfigController {
 
+	float estimatePasswordStrength(String password);
+
 	void validatePassword(String password,
 			ResultHandler<Boolean> resultHandler);
 
 	void changePassword(String password, String newPassword,
 			ResultHandler<Boolean> resultHandler);
+
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
index ac01eba475..92c80ffe50 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
@@ -4,6 +4,7 @@ import android.content.SharedPreferences;
 
 import org.briarproject.bramble.api.crypto.CryptoComponent;
 import org.briarproject.bramble.api.crypto.CryptoExecutor;
+import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -27,14 +28,22 @@ public class PasswordControllerImpl extends ConfigControllerImpl
 
 	protected final Executor cryptoExecutor;
 	protected final CryptoComponent crypto;
+	private final PasswordStrengthEstimator strengthEstimator;
 
 	@Inject
 	PasswordControllerImpl(SharedPreferences briarPrefs,
 			DatabaseConfig databaseConfig,
-			@CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto) {
+			@CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
+			PasswordStrengthEstimator strengthEstimator) {
 		super(briarPrefs, databaseConfig);
 		this.cryptoExecutor = cryptoExecutor;
 		this.crypto = crypto;
+		this.strengthEstimator = strengthEstimator;
+	}
+
+	@Override
+	public float estimatePasswordStrength(String password) {
+		return strengthEstimator.estimateStrength(password);
 	}
 
 	@Override
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
new file mode 100644
index 0000000000..f24cf2daf2
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
@@ -0,0 +1,117 @@
+package org.briarproject.briar.android.login;
+
+import android.os.Bundle;
+import android.support.design.widget.TextInputEditText;
+import android.support.design.widget.TextInputLayout;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.util.UiUtils;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
+
+public class PasswordFragment extends SetupFragment {
+
+	private final static String TAG = PasswordFragment.class.getName();
+
+	private TextInputLayout passwordEntryWrapper;
+	private TextInputLayout passwordConfirmationWrapper;
+	private TextInputEditText passwordEntry;
+	private TextInputEditText passwordConfirmation;
+	private StrengthMeter strengthMeter;
+	private Button nextButton;
+	private ProgressBar progressBar;
+
+	public static PasswordFragment newInstance() {
+		return new PasswordFragment();
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		getActivity().setTitle(getString(R.string.setup_password_intro));
+		View v =
+				inflater.inflate(R.layout.fragment_setup_password, container,
+						false);
+
+		strengthMeter = (StrengthMeter) v.findViewById(R.id.strength_meter);
+		passwordEntryWrapper =
+				(TextInputLayout) v.findViewById(R.id.password_entry_wrapper);
+		passwordEntry = (TextInputEditText) v.findViewById(R.id.password_entry);
+		passwordConfirmationWrapper =
+				(TextInputLayout) v.findViewById(R.id.password_confirm_wrapper);
+		passwordConfirmation =
+				(TextInputEditText) v.findViewById(R.id.password_confirm);
+		nextButton = (Button) v.findViewById(R.id.next);
+		progressBar = (ProgressBar) v.findViewById(R.id.progress);
+
+		passwordEntry.addTextChangedListener(this);
+		passwordConfirmation.addTextChangedListener(this);
+		nextButton.setOnClickListener(this);
+
+		return v;
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+
+		// the controller is not yet available in onCreateView()
+		if (!setupController.needsDozeWhitelisting()) {
+			nextButton.setText(R.string.create_account_button);
+		}
+	}
+
+	@Override
+	protected String getHelpText() {
+		return getString(R.string.setup_password_explanation);
+	}
+
+	@Override
+	public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
+		String password1 = passwordEntry.getText().toString();
+		String password2 = passwordConfirmation.getText().toString();
+		boolean passwordsMatch = password1.equals(password2);
+
+		strengthMeter
+				.setVisibility(password1.length() > 0 ? VISIBLE : INVISIBLE);
+		float strength = setupController.estimatePasswordStrength(password1);
+		strengthMeter.setStrength(strength);
+		boolean strongEnough = strength >= QUITE_WEAK;
+
+		UiUtils.setError(passwordEntryWrapper,
+				getString(R.string.password_too_weak),
+				password1.length() > 0 && !strongEnough);
+		UiUtils.setError(passwordConfirmationWrapper,
+				getString(R.string.passwords_do_not_match),
+				password2.length() > 0 && !passwordsMatch);
+
+		boolean enabled = passwordsMatch && strongEnough;
+		nextButton.setEnabled(enabled);
+		passwordConfirmation.setOnEditorActionListener(enabled ? this : null);
+	}
+
+	@Override
+	public void onClick(View view) {
+		if (!setupController.needsDozeWhitelisting()) {
+			nextButton.setVisibility(INVISIBLE);
+			progressBar.setVisibility(VISIBLE);
+		}
+		String password = passwordEntry.getText().toString();
+		setupController.setPassword(password);
+		setupController.showDozeOrCreateAccount();
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java
index 767a9eb558..17c6f522f3 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java
@@ -1,157 +1,64 @@
 package org.briarproject.briar.android.login;
 
+import android.annotation.TargetApi;
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.design.widget.TextInputLayout;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
 
-import org.briarproject.bramble.util.StringUtils;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.activity.ActivityComponent;
 import org.briarproject.briar.android.activity.BaseActivity;
-import org.briarproject.briar.android.controller.handler.UiResultHandler;
+import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
 import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
-import org.briarproject.briar.android.util.UiUtils;
 
 import javax.inject.Inject;
 
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
-import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 
-public class SetupActivity extends BaseActivity implements OnClickListener,
-		OnEditorActionListener {
+public class SetupActivity extends BaseActivity
+		implements BaseFragmentListener {
 
 	@Inject
 	SetupController setupController;
 
-	private TextInputLayout nicknameEntryWrapper;
-	private TextInputLayout passwordEntryWrapper;
-	private TextInputLayout passwordConfirmationWrapper;
-	private EditText nicknameEntry;
-	private EditText passwordEntry;
-	private EditText passwordConfirmation;
-	private StrengthMeter strengthMeter;
-	private Button createAccountButton;
-	private ProgressBar progress;
-
 	@Override
 	public void onCreate(Bundle state) {
 		super.onCreate(state);
 		// fade-in after splash screen instead of default animation
 		overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
-		setContentView(R.layout.activity_setup);
-
-		nicknameEntryWrapper =
-				(TextInputLayout) findViewById(R.id.nickname_entry_wrapper);
-		passwordEntryWrapper =
-				(TextInputLayout) findViewById(R.id.password_entry_wrapper);
-		passwordConfirmationWrapper =
-				(TextInputLayout) findViewById(R.id.password_confirm_wrapper);
-		nicknameEntry = (EditText) findViewById(R.id.nickname_entry);
-		passwordEntry = (EditText) findViewById(R.id.password_entry);
-		passwordConfirmation = (EditText) findViewById(R.id.password_confirm);
-		strengthMeter = (StrengthMeter) findViewById(R.id.strength_meter);
-		createAccountButton = (Button) findViewById(R.id.create_account);
-		progress = (ProgressBar) findViewById(R.id.progress_wheel);
-
-		TextWatcher tw = new TextWatcher() {
-
-			@Override
-			public void beforeTextChanged(CharSequence s, int start, int count,
-					int after) {
-			}
+		setContentView(R.layout.activity_fragment_container);
 
-			@Override
-			public void onTextChanged(CharSequence s, int start, int before,
-					int count) {
-				enableOrDisableContinueButton();
-			}
-
-			@Override
-			public void afterTextChanged(Editable s) {
-			}
-		};
-
-		nicknameEntry.addTextChangedListener(tw);
-		passwordEntry.addTextChangedListener(tw);
-		passwordConfirmation.addTextChangedListener(tw);
-		passwordConfirmation.setOnEditorActionListener(this);
-		createAccountButton.setOnClickListener(this);
+		if (state == null) {
+			showInitialFragment(AuthorNameFragment.newInstance());
+		}
 	}
 
 	@Override
 	public void injectActivity(ActivityComponent component) {
 		component.inject(this);
+		setupController.setSetupActivity(this);
 	}
 
-	private void enableOrDisableContinueButton() {
-		if (progress == null) return; // Not created yet
-		if (passwordEntry.getText().length() > 0 && passwordEntry.hasFocus())
-			strengthMeter.setVisibility(VISIBLE);
-		else strengthMeter.setVisibility(GONE);
-		String nickname = nicknameEntry.getText().toString();
-		int nicknameLength = StringUtils.toUtf8(nickname).length;
-		String firstPassword = passwordEntry.getText().toString();
-		String secondPassword = passwordConfirmation.getText().toString();
-		boolean passwordsMatch = firstPassword.equals(secondPassword);
-		float strength =
-				setupController.estimatePasswordStrength(firstPassword);
-		strengthMeter.setStrength(strength);
-		UiUtils.setError(nicknameEntryWrapper,
-				getString(R.string.name_too_long),
-				nicknameLength > MAX_AUTHOR_NAME_LENGTH);
-		UiUtils.setError(passwordEntryWrapper,
-				getString(R.string.password_too_weak),
-				firstPassword.length() > 0 && strength < QUITE_WEAK);
-		UiUtils.setError(passwordConfirmationWrapper,
-				getString(R.string.passwords_do_not_match),
-				secondPassword.length() > 0 && !passwordsMatch);
-		createAccountButton.setEnabled(nicknameLength > 0
-				&& nicknameLength <= MAX_AUTHOR_NAME_LENGTH
-				&& passwordsMatch && strength >= QUITE_WEAK);
+	public void showPasswordFragment() {
+		showNextFragment(PasswordFragment.newInstance());
 	}
 
-	@Override
-	public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-		hideSoftKeyboard(v);
-		return true;
+	@TargetApi(23)
+	public void showDozeFragment() {
+		showNextFragment(DozeFragment.newInstance());
 	}
 
-	@Override
-	public void onClick(View view) {
-		// Replace the button with a progress bar
-		createAccountButton.setVisibility(INVISIBLE);
-		progress.setVisibility(VISIBLE);
-		String nickname = nicknameEntry.getText().toString();
-		String password = passwordEntry.getText().toString();
-
-		setupController.storeAuthorInfo(nickname, password,
-				new UiResultHandler<Void>(this) {
-					@Override
-					public void onResultUi(Void result) {
-						showMain();
-					}
-				});
-	}
-
-	private void showMain() {
+	public void showApp() {
 		Intent i = new Intent(this, NavDrawerActivity.class);
 		i.setFlags(FLAG_ACTIVITY_NEW_TASK);
 		startActivity(i);
 		supportFinishAfterTransition();
 		overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out);
 	}
+
+	@Override
+	@Deprecated
+	public void runOnDbThread(Runnable runnable) {
+		throw new RuntimeException("Don't use this deprecated method here.");
+	}
+
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java
index 5e72acb931..96ea253f87 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java
@@ -6,9 +6,25 @@ import org.briarproject.briar.android.controller.handler.ResultHandler;
 @NotNullByDefault
 public interface SetupController {
 
+	void setSetupActivity(SetupActivity setupActivity);
+
+	boolean needsDozeWhitelisting();
+
+	void setAuthorName(String authorName);
+
+	void setPassword(String password);
+
 	float estimatePasswordStrength(String password);
 
-	void storeAuthorInfo(String nickname, String password,
-			ResultHandler<Void> resultHandler);
+	/**
+	 * This should be called after the author name and the password have been
+	 * set. It decides whether to ask for doze exception or create the account
+	 * right away.
+	 */
+	void showDozeOrCreateAccount();
+
+	void createAccount();
+
+	void createAccount(final ResultHandler<Void> resultHandler);
 
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java
index f413ca7361..7fb8a7f7c0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java
@@ -1,6 +1,7 @@
 package org.briarproject.briar.android.login;
 
 import android.content.SharedPreferences;
+import android.support.annotation.Nullable;
 
 import org.briarproject.bramble.api.crypto.CryptoComponent;
 import org.briarproject.bramble.api.crypto.CryptoExecutor;
@@ -9,6 +10,8 @@ import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.briar.android.controller.handler.ResultHandler;
+import org.briarproject.briar.android.controller.handler.UiResultHandler;
+import org.briarproject.briar.android.util.UiUtils;
 
 import java.util.concurrent.Executor;
 
@@ -18,29 +21,75 @@ import javax.inject.Inject;
 public class SetupControllerImpl extends PasswordControllerImpl
 		implements SetupController {
 
-	private final PasswordStrengthEstimator strengthEstimator;
+	@Nullable
+	private String authorName, password;
+	@Nullable
+	private SetupActivity setupActivity;
 
 	@Inject
 	SetupControllerImpl(SharedPreferences briarPrefs,
 			DatabaseConfig databaseConfig,
 			@CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
 			PasswordStrengthEstimator strengthEstimator) {
-		super(briarPrefs, databaseConfig, cryptoExecutor, crypto);
-		this.strengthEstimator = strengthEstimator;
+		super(briarPrefs, databaseConfig, cryptoExecutor, crypto,
+				strengthEstimator);
 	}
 
 	@Override
-	public float estimatePasswordStrength(String password) {
-		return strengthEstimator.estimateStrength(password);
+	public void setSetupActivity(SetupActivity setupActivity) {
+		this.setupActivity = setupActivity;
 	}
 
 	@Override
-	public void storeAuthorInfo(final String nickname, final String password,
-			final ResultHandler<Void> resultHandler) {
+	public boolean needsDozeWhitelisting() {
+		if (setupActivity == null) throw new IllegalStateException();
+		return UiUtils.needsDozeWhitelisting(setupActivity);
+	}
+
+	@Override
+	public void setAuthorName(String authorName) {
+		this.authorName = authorName;
+		if (setupActivity == null) throw new IllegalStateException();
+		setupActivity.showPasswordFragment();
+	}
+
+	@Override
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	@Override
+	public void showDozeOrCreateAccount() {
+		if (setupActivity == null) throw new IllegalStateException();
+		if (needsDozeWhitelisting()) {
+			setupActivity.showDozeFragment();
+		} else {
+			createAccount();
+		}
+	}
+
+	@Override
+	public void createAccount() {
+		final UiResultHandler<Void> resultHandler =
+				new UiResultHandler<Void>(setupActivity) {
+					@Override
+					public void onResultUi(Void result) {
+						if (setupActivity == null)
+							throw new IllegalStateException();
+						setupActivity.showApp();
+					}
+				};
+		createAccount(resultHandler);
+	}
+
+	@Override
+	public void createAccount(final ResultHandler<Void> resultHandler) {
+		if (authorName == null || password == null)
+			throw new IllegalStateException();
 		cryptoExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
-				databaseConfig.setLocalAuthorName(nickname);
+				databaseConfig.setLocalAuthorName(authorName);
 				SecretKey key = crypto.generateSecretKey();
 				databaseConfig.setEncryptionKey(key);
 				String hex = encryptDatabaseKey(key, password);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java
new file mode 100644
index 0000000000..6a30e737a5
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java
@@ -0,0 +1,66 @@
+package org.briarproject.briar.android.login;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.fragment.BaseFragment;
+
+import javax.inject.Inject;
+
+import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
+
+abstract class SetupFragment extends BaseFragment implements TextWatcher,
+		OnEditorActionListener, OnClickListener {
+
+	@Inject
+	SetupController setupController;
+
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		inflater.inflate(R.menu.help_action, menu);
+		super.onCreateOptionsMenu(menu, inflater);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		if (item.getItemId() == R.id.action_help) {
+			showOnboardingDialog(getContext(), getHelpText());
+			return true;
+		} else {
+			return super.onOptionsItemSelected(item);
+		}
+	}
+
+	protected abstract String getHelpText();
+
+	@Override
+	public void beforeTextChanged(CharSequence charSequence, int i, int i1,
+			int i2) {
+		// noop
+	}
+
+	@Override
+	public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
+		// noop
+	}
+
+	@Override
+	public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
+		onClick(textView);
+		return true;
+	}
+
+	@Override
+	public void afterTextChanged(Editable editable) {
+		// noop
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java
index 058d55b551..08c16f7849 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java
@@ -1,14 +1,19 @@
 package org.briarproject.briar.android.navdrawer;
 
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.design.widget.NavigationView;
 import android.support.design.widget.NavigationView.OnNavigationItemSelectedListener;
 import android.support.v4.app.FragmentTransaction;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AlertDialog;
 import android.support.v7.widget.Toolbar;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
@@ -49,9 +54,12 @@ import static android.support.v4.view.GravityCompat.START;
 import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
+import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
 import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
 import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
 import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
+import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent;
+import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
 
 public class NavDrawerActivity extends BriarActivity implements
 		BaseFragmentListener, TransportStateListener,
@@ -134,6 +142,7 @@ public class NavDrawerActivity extends BriarActivity implements
 	}
 
 	@Override
+	@SuppressLint("NewApi")
 	public void onStart() {
 		super.onStart();
 		updateTransports();
@@ -143,6 +152,7 @@ public class NavDrawerActivity extends BriarActivity implements
 				if (expiry != NO) showExpiryWarning(expiry);
 			}
 		});
+		if (needsDozeWhitelisting(this)) requestDozeWhitelisting();
 	}
 
 	private void exitIfStartupFailed(Intent intent) {
@@ -178,7 +188,7 @@ public class NavDrawerActivity extends BriarActivity implements
 	}
 
 	@Override
-	public boolean onNavigationItemSelected(MenuItem item) {
+	public boolean onNavigationItemSelected(@NonNull MenuItem item) {
 		drawerLayout.closeDrawer(START);
 		clearBackStack();
 		loadFragment(item.getItemId());
@@ -311,6 +321,24 @@ public class NavDrawerActivity extends BriarActivity implements
 		expiryWarning.setVisibility(VISIBLE);
 	}
 
+	@TargetApi(23)
+	private void requestDozeWhitelisting() {
+		new AlertDialog.Builder(this, R.style.BriarDialogTheme)
+				.setMessage(R.string.setup_doze_intro)
+				.setPositiveButton(R.string.ok,
+						new DialogInterface.OnClickListener() {
+							@Override
+							public void onClick(DialogInterface dialog,
+									int which) {
+								Intent i = getDozeWhitelistingIntent(
+										NavDrawerActivity.this);
+								startActivityForResult(i,
+										REQUEST_DOZE_WHITELISTING);
+							}
+						})
+				.show();
+	}
+
 	private void initializeTransports(final LayoutInflater inflater) {
 		transports = new ArrayList<>(3);
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
index 74f182cbcb..4482526599 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
@@ -1,14 +1,18 @@
 package org.briarproject.briar.android.util;
 
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.content.Context;
-
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
+import android.os.PowerManager;
 import android.support.design.widget.TextInputLayout;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.text.Html;
 import android.text.Spannable;
@@ -23,14 +27,16 @@ import android.view.View;
 import android.widget.TextView;
 
 import org.briarproject.bramble.api.contact.ContactId;
-import org.briarproject.briar.BuildConfig;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.view.ArticleMovementMethod;
 import org.briarproject.briar.android.widget.LinkDialogFragment;
 
 import javax.annotation.Nullable;
 
-import static android.content.Intent.*;
+import static android.content.Context.POWER_SERVICE;
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
 import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
@@ -38,7 +44,7 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
-import static org.briarproject.briar.BuildConfig.*;
+import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
 import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
 
 public class UiUtils {
@@ -149,4 +155,37 @@ public class UiUtils {
 			}
 		};
 	}
+
+	public static void showOnboardingDialog(Context ctx, String text) {
+		new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme)
+				.setMessage(text)
+				.setNeutralButton(R.string.got_it,
+						new DialogInterface.OnClickListener() {
+							@Override
+							public void onClick(DialogInterface dialog,
+									int which) {
+								dialog.cancel();
+							}
+						})
+				.show();
+	}
+
+	public static boolean needsDozeWhitelisting(Context ctx) {
+		if (Build.VERSION.SDK_INT < 23) return false;
+		PowerManager pm =
+				(PowerManager) ctx.getSystemService(POWER_SERVICE);
+		String packageName = ctx.getPackageName();
+		if (pm == null) throw new AssertionError();
+		return !pm.isIgnoringBatteryOptimizations(packageName);
+	}
+
+	@TargetApi(23)
+	@SuppressLint("BatteryLife")
+	public static Intent getDozeWhitelistingIntent(Context ctx) {
+		Intent i = new Intent();
+		i.setAction(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+		i.setData(Uri.parse("package:" + ctx.getPackageName()));
+		return i;
+	}
+
 }
diff --git a/briar-android/src/main/res/layout/fragment_setup_author_name.xml b/briar-android/src/main/res/layout/fragment_setup_author_name.xml
new file mode 100644
index 0000000000..8f22bc7edf
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_setup_author_name.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:fillViewport="true">
+
+	<android.support.constraint.ConstraintLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:paddingBottom="@dimen/margin_activity_vertical"
+		android:paddingEnd="@dimen/margin_activity_horizontal"
+		android:paddingLeft="@dimen/margin_activity_horizontal"
+		android:paddingRight="@dimen/margin_activity_horizontal"
+		android:paddingStart="@dimen/margin_activity_horizontal"
+		android:paddingTop="@dimen/margin_activity_vertical">
+
+		<android.support.design.widget.TextInputLayout
+			android:id="@+id/nickname_entry_wrapper"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/margin_medium"
+			app:errorEnabled="true"
+			app:hintEnabled="false"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="parent">
+
+			<android.support.design.widget.TextInputEditText
+				android:id="@+id/nickname_entry"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:hint="@string/choose_nickname"
+				android:imeOptions="actionNext"
+				android:inputType="text|textCapWords"
+				android:maxLines="1"/>
+
+			<requestFocus/>
+		</android.support.design.widget.TextInputLayout>
+
+		<Button
+			android:id="@+id/next"
+			style="@style/BriarButton.Default"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/margin_activity_horizontal"
+			android:enabled="false"
+			android:text="@string/setup_next"
+			app:layout_constraintBottom_toBottomOf="parent"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/nickname_entry_wrapper"
+			app:layout_constraintVertical_bias="1.0"
+			tools:enabled="true"/>
+
+	</android.support.constraint.ConstraintLayout>
+
+</ScrollView>
diff --git a/briar-android/src/main/res/layout/fragment_setup_doze.xml b/briar-android/src/main/res/layout/fragment_setup_doze.xml
new file mode 100644
index 0000000000..fedb98f6db
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_setup_doze.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:fillViewport="true">
+
+	<android.support.constraint.ConstraintLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:paddingBottom="@dimen/margin_activity_vertical"
+		android:paddingEnd="@dimen/margin_activity_horizontal"
+		android:paddingLeft="@dimen/margin_activity_horizontal"
+		android:paddingRight="@dimen/margin_activity_horizontal"
+		android:paddingStart="@dimen/margin_activity_horizontal"
+		android:paddingTop="@dimen/margin_activity_vertical">
+
+		<TextView
+			android:id="@+id/setup_explanation"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:text="@string/setup_doze_intro"
+			android:textSize="@dimen/text_size_medium"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toTopOf="parent"/>
+
+		<Button
+			android:id="@+id/dozeButton"
+			style="@style/BriarButton.Default"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/margin_activity_horizontal"
+			android:text="@string/setup_doze_button"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/setup_explanation"/>
+
+		<ProgressBar
+			android:id="@+id/progress"
+			style="?android:attr/progressBarStyle"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginBottom="16dp"
+			android:layout_marginTop="16dp"
+			android:visibility="invisible"
+			app:layout_constraintBottom_toBottomOf="@+id/dozeButton"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toTopOf="@+id/dozeButton"/>
+
+	</android.support.constraint.ConstraintLayout>
+
+</ScrollView>
diff --git a/briar-android/src/main/res/layout/activity_setup.xml b/briar-android/src/main/res/layout/fragment_setup_password.xml
similarity index 54%
rename from briar-android/src/main/res/layout/activity_setup.xml
rename to briar-android/src/main/res/layout/fragment_setup_password.xml
index b1120debd9..5a925750e5 100644
--- a/briar-android/src/main/res/layout/activity_setup.xml
+++ b/briar-android/src/main/res/layout/fragment_setup_password.xml
@@ -5,12 +5,11 @@
 	xmlns:tools="http://schemas.android.com/tools"
 	android:layout_width="match_parent"
 	android:layout_height="match_parent"
-	tools:context=".android.login.SetupActivity">
+	android:fillViewport="true">
 
-	<RelativeLayout
+	<android.support.constraint.ConstraintLayout
 		android:layout_width="match_parent"
 		android:layout_height="wrap_content"
-		android:orientation="vertical"
 		android:paddingBottom="@dimen/margin_activity_vertical"
 		android:paddingEnd="@dimen/margin_activity_horizontal"
 		android:paddingLeft="@dimen/margin_activity_horizontal"
@@ -18,38 +17,16 @@
 		android:paddingStart="@dimen/margin_activity_horizontal"
 		android:paddingTop="@dimen/margin_activity_vertical">
 
-		<TextView
-			android:id="@+id/setup_explanation"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:text="@string/setup_explanation"/>
-
-		<android.support.design.widget.TextInputLayout
-			android:id="@+id/nickname_entry_wrapper"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:layout_below="@+id/setup_explanation"
-			android:layout_centerHorizontal="true"
-			android:layout_marginTop="@dimen/margin_medium"
-			app:errorEnabled="true">
-
-			<android.support.design.widget.TextInputEditText
-				android:id="@+id/nickname_entry"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:hint="@string/choose_nickname"
-				android:imeOptions="actionNext"
-				android:inputType="text|textCapWords"
-				android:maxLines="1"/>
-		</android.support.design.widget.TextInputLayout>
-
 		<android.support.design.widget.TextInputLayout
 			android:id="@+id/password_entry_wrapper"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:layout_below="@+id/nickname_entry_wrapper"
-			android:layout_centerHorizontal="true"
-			app:errorEnabled="true">
+			android:layout_marginTop="8dp"
+			app:errorEnabled="true"
+			app:hintEnabled="false"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toTopOf="parent">
 
 			<android.support.design.widget.TextInputEditText
 				android:id="@+id/password_entry"
@@ -58,55 +35,70 @@
 				android:hint="@string/choose_password"
 				android:imeOptions="actionNext"
 				android:inputType="textPassword"
-				android:maxLines="1"/>
+				android:maxLines="1">
+
+				<requestFocus/>
+			</android.support.design.widget.TextInputEditText>
+
 		</android.support.design.widget.TextInputLayout>
 
 		<org.briarproject.briar.android.login.StrengthMeter
 			android:id="@+id/strength_meter"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:layout_below="@+id/password_entry_wrapper"
-			android:layout_centerHorizontal="true"
-			android:layout_marginBottom="@dimen/margin_medium"
-			android:visibility="gone"/>
+			android:layout_marginTop="8dp"
+			android:visibility="invisible"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/password_entry_wrapper"
+			tools:visibility="visible"/>
 
 		<android.support.design.widget.TextInputLayout
 			android:id="@+id/password_confirm_wrapper"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:layout_below="@+id/strength_meter"
-			android:layout_centerHorizontal="true"
-			app:errorEnabled="true">
+			android:layout_marginTop="8dp"
+			app:errorEnabled="true"
+			app:hintEnabled="false"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/strength_meter">
 
 			<android.support.design.widget.TextInputEditText
 				android:id="@+id/password_confirm"
 				android:layout_width="match_parent"
 				android:layout_height="wrap_content"
 				android:hint="@string/confirm_password"
-				android:imeOptions="actionDone"
 				android:inputType="textPassword"
 				android:maxLines="1"/>
 		</android.support.design.widget.TextInputLayout>
 
 		<Button
-			android:id="@+id/create_account"
+			android:id="@+id/next"
 			style="@style/BriarButton.Default"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:layout_below="@+id/password_confirm_wrapper"
-			android:layout_centerHorizontal="true"
 			android:enabled="false"
-			android:text="@string/create_account_button"/>
+			android:text="@string/setup_next"
+			app:layout_constraintBottom_toBottomOf="parent"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/password_confirm_wrapper"
+			app:layout_constraintVertical_bias="1.0"
+			tools:enabled="true"/>
 
 		<ProgressBar
-			android:id="@+id/progress_wheel"
+			android:id="@+id/progress"
 			style="?android:attr/progressBarStyle"
 			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
-			android:layout_alignTop="@id/create_account"
-			android:layout_centerHorizontal="true"
-			android:visibility="invisible"/>
+			android:visibility="invisible"
+			app:layout_constraintBottom_toBottomOf="parent"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/password_confirm_wrapper"
+			app:layout_constraintVertical_bias="1.0"/>
 
-	</RelativeLayout>
+	</android.support.constraint.ConstraintLayout>
 
-</ScrollView>
\ No newline at end of file
+</ScrollView>
diff --git a/briar-android/src/main/res/menu/help_action.xml b/briar-android/src/main/res/menu/help_action.xml
new file mode 100644
index 0000000000..a0e4c3d2c8
--- /dev/null
+++ b/briar-android/src/main/res/menu/help_action.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+
+	<item
+		android:id="@+id/action_help"
+		android:icon="@drawable/ic_info_white"
+		android:title="@string/more_info"
+		app:showAsAction="always"/>
+
+</menu>
\ No newline at end of file
diff --git a/briar-android/src/main/res/values-bg/strings.xml b/briar-android/src/main/res/values-bg/strings.xml
index 62169ffced..747ec467f3 100644
--- a/briar-android/src/main/res/values-bg/strings.xml
+++ b/briar-android/src/main/res/values-bg/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Briar регистрация</string>
-  <string name="setup_explanation">Briar профилът се съхранява криптиран във вашето устройство, а не в облака. Ако деинсталирате Briar или забравите паролата си, няма как да възстановите профила и данните си.</string>
+  <string name="setup_password_explanation">Briar профилът се съхранява криптиран във вашето устройство, а не в облака. Ако деинсталирате Briar или забравите паролата си, няма как да възстановите профила и данните си.</string>
   <string name="choose_nickname">Изберете име</string>
   <string name="choose_password">Изберете парола</string>
   <string name="confirm_password">Потвърдете парола</string>
diff --git a/briar-android/src/main/res/values-ca/strings.xml b/briar-android/src/main/res/values-ca/strings.xml
index e10b2fe889..58fe1a2c63 100644
--- a/briar-android/src/main/res/values-ca/strings.xml
+++ b/briar-android/src/main/res/values-ca/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Configuració de Briar</string>
-  <string name="setup_explanation">El teu compte de Briar està encriptat al teu dispositiu, no al núvol. Si desinstal·les Briar o oblides la contrasenya, no hi ha manera de recuperar el teu compte i les teves dades.</string>
+  <string name="setup_password_explanation">El teu compte de Briar està encriptat al teu dispositiu, no al núvol. Si desinstal·les Briar o oblides la contrasenya, no hi ha manera de recuperar el teu compte i les teves dades.</string>
   <string name="choose_nickname">Trieu el sobrenom</string>
   <string name="choose_password">Trieu la contrasenya</string>
   <string name="confirm_password">Confirmeu la contrasenya</string>
diff --git a/briar-android/src/main/res/values-de/strings.xml b/briar-android/src/main/res/values-de/strings.xml
index 9b4a0648af..0948375f45 100644
--- a/briar-android/src/main/res/values-de/strings.xml
+++ b/briar-android/src/main/res/values-de/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Briar einrichten</string>
-  <string name="setup_explanation">Dein Briar-Konto wird verschlüsselt auf deinem Gerät gespeichert und nicht mit der \"Cloud\" synchronisiert. Wenn du Briar deinstallierst oder dein Passwort vergisst, können das Konto und deine Daten nicht wiederhergestellt werden.</string>
+  <string name="setup_password_explanation">Dein Briar-Konto wird verschlüsselt auf deinem Gerät gespeichert und nicht mit der \"Cloud\" synchronisiert. Wenn du Briar deinstallierst oder dein Passwort vergisst, können das Konto und deine Daten nicht wiederhergestellt werden.</string>
   <string name="choose_nickname">Wähle deinen Benutzernamen</string>
   <string name="choose_password">Wähle dein Passwort</string>
   <string name="confirm_password">Passwort bestätigen</string>
diff --git a/briar-android/src/main/res/values-es/strings.xml b/briar-android/src/main/res/values-es/strings.xml
index 16de8f81a9..d662b47463 100644
--- a/briar-android/src/main/res/values-es/strings.xml
+++ b/briar-android/src/main/res/values-es/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Configuración de Briar</string>
-  <string name="setup_explanation">Tu cuenta de Briar se almacena de manera cifrada en tu dispositivo y no en ninguna nube. Si desinstalas Briar u olvidas tu contraseña, no podrás recuperar ni tu cuenta ni tus datos.</string>
+  <string name="setup_password_explanation">Tu cuenta de Briar se almacena de manera cifrada en tu dispositivo y no en ninguna nube. Si desinstalas Briar u olvidas tu contraseña, no podrás recuperar ni tu cuenta ni tus datos.</string>
   <string name="choose_nickname">Elige tu nombre de usuario</string>
   <string name="choose_password">Elige tu contraseña</string>
   <string name="confirm_password">Confirma tu contraseña</string>
diff --git a/briar-android/src/main/res/values-eu/strings.xml b/briar-android/src/main/res/values-eu/strings.xml
index 9e188e369a..7e39e8d2c8 100644
--- a/briar-android/src/main/res/values-eu/strings.xml
+++ b/briar-android/src/main/res/values-eu/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Briar ezarpena</string>
-  <string name="setup_explanation">Zure Briar kontua zure gailuan zifratuta gordetzen da, ez hodeian. Briar desinstalatzen baduzu edo pasahitza ahazten baduzu, ez dago zure kontua eta datuak berreskuratzeko modurik.</string>
+  <string name="setup_password_explanation">Zure Briar kontua zure gailuan zifratuta gordetzen da, ez hodeian. Briar desinstalatzen baduzu edo pasahitza ahazten baduzu, ez dago zure kontua eta datuak berreskuratzeko modurik.</string>
   <string name="choose_nickname">Hautatu zure ezizena</string>
   <string name="choose_password">Hautatu zure pasahitza</string>
   <string name="confirm_password">Berretsi zure pasahitza</string>
diff --git a/briar-android/src/main/res/values-fi/strings.xml b/briar-android/src/main/res/values-fi/strings.xml
index b45fcb161b..88787a8935 100644
--- a/briar-android/src/main/res/values-fi/strings.xml
+++ b/briar-android/src/main/res/values-fi/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Briarin asetus</string>
-  <string name="setup_explanation">Sinun Briar tili on tallennettu salattuna sinun laitteelle, ei pilvipalvelimelle. Jos poistat Briarin asennuksen tai unohdat salasanasi, ei ole mitään keinoa jolla tilisi tai dataasi voisi palauttaa.</string>
+  <string name="setup_password_explanation">Sinun Briar tili on tallennettu salattuna sinun laitteelle, ei pilvipalvelimelle. Jos poistat Briarin asennuksen tai unohdat salasanasi, ei ole mitään keinoa jolla tilisi tai dataasi voisi palauttaa.</string>
   <string name="choose_nickname">Valitse nimimerkki</string>
   <string name="choose_password">Valitse salasana</string>
   <string name="confirm_password">Vahvista salasana</string>
diff --git a/briar-android/src/main/res/values-fr/strings.xml b/briar-android/src/main/res/values-fr/strings.xml
index 8046f385fe..67ef5fd286 100644
--- a/briar-android/src/main/res/values-fr/strings.xml
+++ b/briar-android/src/main/res/values-fr/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Configuration de Briar</string>
-  <string name="setup_explanation">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le nuage. Si vous désinstallez Briar ou oubliez votre mot de passe, votre compte et vos données sont irrécupérables.</string>
+  <string name="setup_password_explanation">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le nuage. Si vous désinstallez Briar ou oubliez votre mot de passe, votre compte et vos données sont irrécupérables.</string>
   <string name="choose_nickname">Choisir votre pseudonyme</string>
   <string name="choose_password">Choisir votre mot de passe</string>
   <string name="confirm_password">Confirmer votre mot de passe</string>
diff --git a/briar-android/src/main/res/values-gl/strings.xml b/briar-android/src/main/res/values-gl/strings.xml
index 163112a9fc..db68f1819b 100644
--- a/briar-android/src/main/res/values-gl/strings.xml
+++ b/briar-android/src/main/res/values-gl/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Configuración de Briar</string>
-  <string name="setup_explanation">Briar almacena a súa configuración encriptada no dispositivo, non na nube. Se desinstala Briar ou esquece a súa clave, non hai forma de recuperar a súa conta e datos.</string>
+  <string name="setup_password_explanation">Briar almacena a súa configuración encriptada no dispositivo, non na nube. Se desinstala Briar ou esquece a súa clave, non hai forma de recuperar a súa conta e datos.</string>
   <string name="choose_nickname">Escolle o teu alcume</string>
   <string name="choose_password">Escolle a túa clave</string>
   <string name="confirm_password">Confirma a túa clave</string>
diff --git a/briar-android/src/main/res/values-hi/strings.xml b/briar-android/src/main/res/values-hi/strings.xml
index 75a65240bb..272d6edbb4 100644
--- a/briar-android/src/main/res/values-hi/strings.xml
+++ b/briar-android/src/main/res/values-hi/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">ब्रियर सेटअप</string>
-  <string name="setup_explanation">आपका ब्रियर खाता आपके डिवाइस पर एन्क्रिप्ट किया गया है, न कि क्लाउड में। यदि आप बिअर की स्थापना रद्द करते हैं या अपना पासवर्ड भूल जाते हैं, तो आपके खाता और आपके डेटा को पुनर्प्राप्त करने का कोई तरीका नहीं है।</string>
+  <string name="setup_password_explanation">आपका ब्रियर खाता आपके डिवाइस पर एन्क्रिप्ट किया गया है, न कि क्लाउड में। यदि आप बिअर की स्थापना रद्द करते हैं या अपना पासवर्ड भूल जाते हैं, तो आपके खाता और आपके डेटा को पुनर्प्राप्त करने का कोई तरीका नहीं है।</string>
   <string name="choose_nickname">आपका मुंहबोला नाम चुनें</string>
   <string name="choose_password">अपना पासवर्ड चुनें</string>
   <string name="confirm_password">अपने पासवर्ड की पुष्टि करें</string>
diff --git a/briar-android/src/main/res/values-it/strings.xml b/briar-android/src/main/res/values-it/strings.xml
index 55bbdec45e..11af431345 100644
--- a/briar-android/src/main/res/values-it/strings.xml
+++ b/briar-android/src/main/res/values-it/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Impostazione Briar</string>
-  <string name="setup_explanation">Il tuo account Briar si trova criptato sul tuo dispositivo e non nel cloud. Se disinstalli Briar o dimentichi la tua password, non c\'è modo di recuperare il tuo account o i tuoi dati.</string>
+  <string name="setup_password_explanation">Il tuo account Briar si trova criptato sul tuo dispositivo e non nel cloud. Se disinstalli Briar o dimentichi la tua password, non c\'è modo di recuperare il tuo account o i tuoi dati.</string>
   <string name="choose_nickname">Scegli il tuo nickname</string>
   <string name="choose_password">Scegli la tua password</string>
   <string name="confirm_password">Conferma la tua password</string>
diff --git a/briar-android/src/main/res/values-nb/strings.xml b/briar-android/src/main/res/values-nb/strings.xml
index e0bea86d8f..91f605a602 100644
--- a/briar-android/src/main/res/values-nb/strings.xml
+++ b/briar-android/src/main/res/values-nb/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Oppsett av Briar</string>
-  <string name="setup_explanation">Din Briar-konto er lagret kryptert på din enhet, ikke i skyen. Hvis du avinstallerer Briar eller glemmer passordet ditt, går det ikke an å gjenopprette kontoen og dataen din.</string>
+  <string name="setup_password_explanation">Din Briar-konto er lagret kryptert på din enhet, ikke i skyen. Hvis du avinstallerer Briar eller glemmer passordet ditt, går det ikke an å gjenopprette kontoen og dataen din.</string>
   <string name="choose_nickname">Velg kallenavn</string>
   <string name="choose_password">Velg passord</string>
   <string name="confirm_password">Bekreft passord</string>
diff --git a/briar-android/src/main/res/values-oc/strings.xml b/briar-android/src/main/res/values-oc/strings.xml
index 506da45b35..af0cf51c99 100644
--- a/briar-android/src/main/res/values-oc/strings.xml
+++ b/briar-android/src/main/res/values-oc/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Configuracion de Briar</string>
-  <string name="setup_explanation">Vòstre compte Briar es salvat e chifrat sus vòstre aparelh, non pas sus un servidor alonhat \"cloud\". Se desinstalletz Briar o oblidetz vòstre senhal, vòstre compte e vòstras donadas serà pas possible de los recuperar.</string>
+  <string name="setup_password_explanation">Vòstre compte Briar es salvat e chifrat sus vòstre aparelh, non pas sus un servidor alonhat \"cloud\". Se desinstalletz Briar o oblidetz vòstre senhal, vòstre compte e vòstras donadas serà pas possible de los recuperar.</string>
   <string name="choose_nickname">Causir un escais-nom</string>
   <string name="choose_password">Causir un senhal</string>
   <string name="confirm_password">Confirmar lo senhal</string>
diff --git a/briar-android/src/main/res/values-pt-rBR/strings.xml b/briar-android/src/main/res/values-pt-rBR/strings.xml
index 3dc461fb0a..68a5e6f3ba 100644
--- a/briar-android/src/main/res/values-pt-rBR/strings.xml
+++ b/briar-android/src/main/res/values-pt-rBR/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Configuração de Briar</string>
-  <string name="setup_explanation">Sua conta Briar é armazenada criptografada apenas no seu dispositivo, não na nuvem. Se você Desinstalar o Briar ou esquecer sua senha, não será possível recuperar sua conta e seus dados.</string>
+  <string name="setup_password_explanation">Sua conta Briar é armazenada criptografada apenas no seu dispositivo, não na nuvem. Se você Desinstalar o Briar ou esquecer sua senha, não será possível recuperar sua conta e seus dados.</string>
   <string name="choose_nickname">Escolha seu apelido</string>
   <string name="choose_password">Escolha sua senha</string>
   <string name="confirm_password">Confirme sua senha</string>
diff --git a/briar-android/src/main/res/values-ru/strings.xml b/briar-android/src/main/res/values-ru/strings.xml
index 9e2c749a56..f053c81f82 100644
--- a/briar-android/src/main/res/values-ru/strings.xml
+++ b/briar-android/src/main/res/values-ru/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Настройка Briar</string>
-  <string name="setup_explanation">Ваша учетная запись Briar хранится в зашифрованном виде только на устройстве. Если вы удалите Briar или забудете пароль, то не сможете восстановить свою учетную запись и данные.</string>
+  <string name="setup_password_explanation">Ваша учетная запись Briar хранится в зашифрованном виде только на устройстве. Если вы удалите Briar или забудете пароль, то не сможете восстановить свою учетную запись и данные.</string>
   <string name="choose_nickname">Выберите псевдоним</string>
   <string name="choose_password">Выберите пароль</string>
   <string name="confirm_password">Подтвердите пароль</string>
diff --git a/briar-android/src/main/res/values-sq/strings.xml b/briar-android/src/main/res/values-sq/strings.xml
index 792c5f7c03..b88bb515c0 100644
--- a/briar-android/src/main/res/values-sq/strings.xml
+++ b/briar-android/src/main/res/values-sq/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Rregullimi i Briar-it</string>
-  <string name="setup_explanation">Llogaria juaj Briar depozitohet e fshehtëzuar në pajisjen tuaj, jo në re. Nëse e çinstaloni Briar-in ose harroni fjalëkalimin tuaj, nuk ka rrugë për ta rimarrë llogarinë tuaj dhe të dhënat tuaja.</string>
+  <string name="setup_password_explanation">Llogaria juaj Briar depozitohet e fshehtëzuar në pajisjen tuaj, jo në re. Nëse e çinstaloni Briar-in ose harroni fjalëkalimin tuaj, nuk ka rrugë për ta rimarrë llogarinë tuaj dhe të dhënat tuaja.</string>
   <string name="choose_nickname">Zgjidhni nofkën tuaj</string>
   <string name="choose_password">Zgjidhni fjalëkalimin tuaj</string>
   <string name="confirm_password">Ripohoni fjalëkalimin tuaj</string>
diff --git a/briar-android/src/main/res/values-sr/strings.xml b/briar-android/src/main/res/values-sr/strings.xml
index 86f1fa3e75..6bf0c45854 100644
--- a/briar-android/src/main/res/values-sr/strings.xml
+++ b/briar-android/src/main/res/values-sr/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Briar Podešavanje</string>
-  <string name="setup_explanation">Vaš Briar račun je kriptovan i sačuvan na vašem uređaju, ne u server-oblaku. Ako deinstalirate Briar ili zaboravite šifru, nema načina da povratite vaš račun ili vaše podatke.</string>
+  <string name="setup_password_explanation">Vaš Briar račun je kriptovan i sačuvan na vašem uređaju, ne u server-oblaku. Ako deinstalirate Briar ili zaboravite šifru, nema načina da povratite vaš račun ili vaše podatke.</string>
   <string name="choose_nickname">Izaberite vaš nadimak</string>
   <string name="choose_password">Izaberite vašu šifru</string>
   <string name="confirm_password">Potvrdite vašu šifru</string>
diff --git a/briar-android/src/main/res/values-tr/strings.xml b/briar-android/src/main/res/values-tr/strings.xml
index 19b78908a9..9b7c1ac5a6 100644
--- a/briar-android/src/main/res/values-tr/strings.xml
+++ b/briar-android/src/main/res/values-tr/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">Briar Kurulum</string>
-  <string name="setup_explanation">Briar hesabınız bulutta değil, cihazınızda şifreli olarak saklanır. Briar\'ı kaldırırsanız veya şifrenizi unutursanız, hesabınızı ve verilerinizi kurtarmanın bir yolu yoktur.</string>
+  <string name="setup_password_explanation">Briar hesabınız bulutta değil, cihazınızda şifreli olarak saklanır. Briar\'ı kaldırırsanız veya şifrenizi unutursanız, hesabınızı ve verilerinizi kurtarmanın bir yolu yoktur.</string>
   <string name="choose_nickname">Kullanıcı adınızı belirleyin</string>
   <string name="choose_password">Parolanızı belirleyin</string>
   <string name="confirm_password">Parolanızı doğrulayın</string>
diff --git a/briar-android/src/main/res/values-zh-rCN/strings.xml b/briar-android/src/main/res/values-zh-rCN/strings.xml
index f99ebc2793..d7c1275771 100644
--- a/briar-android/src/main/res/values-zh-rCN/strings.xml
+++ b/briar-android/src/main/res/values-zh-rCN/strings.xml
@@ -2,7 +2,7 @@
 <resources>
   <!--Setup-->
   <string name="setup_title">安装 Briar</string>
-  <string name="setup_explanation">您的 Briar 账号只会被加密储存在您的设备上,不会被上传。如果您卸载了 Briar 或忘记了密码,将无法恢复您的账号和数据。</string>
+  <string name="setup_password_explanation">您的 Briar 账号只会被加密储存在您的设备上,不会被上传。如果您卸载了 Briar 或忘记了密码,将无法恢复您的账号和数据。</string>
   <string name="choose_nickname">设置昵称</string>
   <string name="choose_password">设置密码</string>
   <string name="confirm_password">确认密码</string>
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index dd04625ccb..0c800fe0c5 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -2,8 +2,15 @@
 <resources>
 
 	<!-- Setup -->
-	<string name="setup_title">Briar Setup</string>
-	<string name="setup_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you uninstall Briar or forget your password, there\'s no way to recover your account and your data.</string>
+	<string name="setup_title">Welcome to Briar</string>
+	<string name="setup_name_explanation">Choose your user name wisely. You can not change it later and it will be shown next to content you post.\n\nWhen this content is shared, strangers might see your name.</string>
+	<string name="setup_next">Next</string>
+	<string name="setup_password_intro">Choose a Password</string>
+	<string name="setup_password_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you forget your password or uninstall Briar, there\'s no way to recover your account.\n\nChoose a long password that\'s hard to guess, such as four random words, or ten random letters, numbers and symbols.</string>
+	<string name="setup_doze_title">Background Connections</string>
+	<string name="setup_doze_intro">To receive messages, Briar needs to stay connected in the background.</string>
+	<string name="setup_doze_explanation">To receive messages, Briar needs to stay connected in the background. Please disable battery optimizations so Briar can stay connected.</string>
+	<string name="setup_doze_button">Allow Connections</string>
 	<string name="choose_nickname">Choose your nickname</string>
 	<string name="choose_password">Choose your password</string>
 	<string name="confirm_password">Confirm your password</string>
@@ -11,6 +18,7 @@
 	<string name="password_too_weak">Password is too weak</string>
 	<string name="passwords_do_not_match">Passwords do not match</string>
 	<string name="create_account_button">Create Account</string>
+	<string name="more_info">More Information</string>
 
 	<!-- Login -->
 	<string name="enter_password">Enter your password:</string>
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java
index 24c84835ac..af3cccf261 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java
@@ -57,8 +57,6 @@ public class ChangePasswordActivityTest {
 
 	@Mock
 	private PasswordController passwordController;
-	@Mock
-	private SetupController setupController;
 	@Captor
 	private ArgumentCaptor<ResultHandler<Boolean>> resultCaptor;
 
@@ -71,14 +69,18 @@ public class ChangePasswordActivityTest {
 				.findViewById(R.id.new_password_confirm_wrapper);
 		currentPassword = (EditText) changePasswordActivity
 				.findViewById(R.id.current_password_entry);
-		newPassword = (EditText) changePasswordActivity
-				.findViewById(R.id.new_password_entry);
-		newPasswordConfirmation = (EditText) changePasswordActivity
-				.findViewById(R.id.new_password_confirm);
-		strengthMeter = (StrengthMeter) changePasswordActivity
-				.findViewById(R.id.strength_meter);
-		changePasswordButton = (Button) changePasswordActivity
-				.findViewById(R.id.change_password);
+		newPassword =
+				(EditText) changePasswordActivity
+						.findViewById(R.id.new_password_entry);
+		newPasswordConfirmation =
+				(EditText) changePasswordActivity
+						.findViewById(R.id.new_password_confirm);
+		strengthMeter =
+				(StrengthMeter) changePasswordActivity
+						.findViewById(R.id.strength_meter);
+		changePasswordButton =
+				(Button) changePasswordActivity
+						.findViewById(R.id.change_password);
 	}
 
 	private void testStrengthMeter(String pass, float strength, int color) {
@@ -110,12 +112,9 @@ public class ChangePasswordActivityTest {
 
 	@Test
 	public void testChangePasswordUI() {
-		PasswordController mockedPasswordController = this.passwordController;
-		SetupController mockedSetupController = this.setupController;
-		changePasswordActivity.setPasswordController(mockedPasswordController);
-		changePasswordActivity.setSetupController(mockedSetupController);
+		changePasswordActivity.setPasswordController(passwordController);
 		// Mock strong password strength answer
-		when(mockedSetupController.estimatePasswordStrength(anyString()))
+		when(passwordController.estimatePasswordStrength(anyString()))
 				.thenReturn(STRONG);
 		String curPass = "old.password";
 		String safePass = "really.safe.password";
@@ -127,7 +126,7 @@ public class ChangePasswordActivityTest {
 		changePasswordButton.performClick();
 		// Verify that the controller's method was called with the correct
 		// params and get the callback
-		verify(mockedPasswordController, times(1))
+		verify(passwordController, times(1))
 				.changePassword(eq(curPass), eq(safePass),
 						resultCaptor.capture());
 		// execute the callbacks
@@ -139,23 +138,24 @@ public class ChangePasswordActivityTest {
 	public void testPasswordChange() {
 		PasswordController passwordController =
 				changePasswordActivity.getPasswordController();
-		SetupController setupController =
-				changePasswordActivity.getSetupController();
+
+		TestSetupActivity setupActivity =
+				Robolectric.setupActivity(TestSetupActivity.class);
+		SetupController setupController = setupActivity.getController();
+		setupController.setAuthorName("nick");
+		setupController.setPassword("some.old.pass");
 		// mock a resulthandler
-		ResultHandler<Void> resultHandler =
-				(ResultHandler<Void>) mock(ResultHandler.class);
-		setupController.storeAuthorInfo("nick", "some.old.pass", resultHandler);
-		// blocking verification call with timeout that waits until the mocked
-		// result gets called with handle 0L, the expected value
+		ResultHandler<Void> resultHandler = mock(ResultHandler.class);
+		setupController.createAccount(resultHandler);
 		verify(resultHandler, timeout(TIMEOUT_MS).times(1)).onResult(null);
+
 		SharedPreferences prefs = changePasswordActivity
 				.getSharedPreferences("db", Context.MODE_PRIVATE);
 		// Confirm database key
 		assertTrue(prefs.contains("key"));
 		String oldKey = prefs.getString("key", null);
 		// mock a resulthandler
-		ResultHandler<Boolean> resultHandler2 =
-				(ResultHandler<Boolean>) mock(ResultHandler.class);
+		ResultHandler<Boolean> resultHandler2 = mock(ResultHandler.class);
 		passwordController.changePassword("some.old.pass", "some.strong.pass",
 				resultHandler2);
 		// blocking verification call with timeout that waits until the mocked
@@ -164,14 +164,14 @@ public class ChangePasswordActivityTest {
 		// Confirm database key
 		assertTrue(prefs.contains("key"));
 		assertNotEquals(oldKey, prefs.getString("key", null));
-		// Note that Robolectric uses its own persistant storage that it
+		// Note that Robolectric uses its own persistent storage that it
 		// wipes clean after each test run, no need to clean up manually.
 	}
 
 	@Test
 	public void testStrengthMeter() {
-		SetupController controller =
-				changePasswordActivity.getSetupController();
+		PasswordController controller =
+				changePasswordActivity.getPasswordController();
 
 		String strongPass = "very.strong.password.123";
 		String weakPass = "we";
@@ -188,9 +188,9 @@ public class ChangePasswordActivityTest {
 	@Test
 	public void testStrengthMeterUI() {
 		Assert.assertNotNull(changePasswordActivity);
-		// replace the setup controller with our mocked copy
-		SetupController mockedController = this.setupController;
-		changePasswordActivity.setSetupController(mockedController);
+		// replace the password controller with our mocked copy
+		PasswordController mockedController = this.passwordController;
+		changePasswordActivity.setPasswordController(mockedController);
 		// Mock answers for UI testing only
 		when(mockedController.estimatePasswordStrength("strong")).thenReturn(
 				STRONG);
@@ -220,4 +220,5 @@ public class ChangePasswordActivityTest {
 		Mockito.verify(mockedController, Mockito.times(1))
 				.estimatePasswordStrength(eq("empty"));
 	}
+
 }
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java
index 7841d792bf..38c45f044e 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java
@@ -19,30 +19,24 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricGradleTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowActivity;
+import org.robolectric.shadows.ShadowLooper;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 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.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(RobolectricGradleTestRunner.class)
@@ -62,40 +56,32 @@ public class SetupActivityTest {
 	private StrengthMeter strengthMeter;
 	private Button createAccountButton;
 
-	@Mock
-	private SetupController setupController;
-	@Captor
-	private ArgumentCaptor<ResultHandler<Void>> authorCaptor;
-
 	@Before
 	public void setUp() {
 		MockitoAnnotations.initMocks(this);
 		setupActivity = Robolectric.setupActivity(TestSetupActivity.class);
 		nicknameEntryWrapper = (TextInputLayout) setupActivity
 				.findViewById(R.id.nickname_entry_wrapper);
-		passwordConfirmationWrapper = (TextInputLayout) setupActivity
-				.findViewById(R.id.password_confirm_wrapper);
 		nicknameEntry =
 				(EditText) setupActivity.findViewById(R.id.nickname_entry);
-		passwordEntry =
-				(EditText) setupActivity.findViewById(R.id.password_entry);
-		passwordConfirmation =
-				(EditText) setupActivity.findViewById(R.id.password_confirm);
-		strengthMeter =
-				(StrengthMeter) setupActivity.findViewById(R.id.strength_meter);
-		createAccountButton =
-				(Button) setupActivity.findViewById(R.id.create_account);
+		createAccountButton = (Button) setupActivity.findViewById(R.id.next);
 	}
 
-	private void testStrengthMeter(String pass, float strength, int color) {
-		passwordEntry.setText(pass);
-		assertEquals(strengthMeter.getProgress(),
-				(int) (strengthMeter.getMax() * strength));
-		assertEquals(color, strengthMeter.getColor());
+	@Test
+	public void testNicknameUI() {
+		Assert.assertNotNull(setupActivity);
+		String longNick =
+				Strings.padEnd("*", AuthorConstants.MAX_AUTHOR_NAME_LENGTH + 1,
+						'*');
+		nicknameEntry.setText(longNick);
+		// Nickname should be too long
+		assertEquals(nicknameEntryWrapper.getError(),
+				setupActivity.getString(R.string.name_too_long));
 	}
 
 	@Test
 	public void testPasswordMatchUI() {
+		proceedToPasswordFragment();
 		// Password mismatch
 		passwordEntry.setText("really.safe.password");
 		passwordConfirmation.setText("really.safe.pass");
@@ -108,67 +94,50 @@ public class SetupActivityTest {
 		// Confirm that the password mismatch error message is not visible
 		Assert.assertNotEquals(passwordConfirmationWrapper.getError(),
 				setupActivity.getString(R.string.passwords_do_not_match));
-		// Nick has not been set, expect the button to be disabled
-		assertEquals(createAccountButton.isEnabled(), false);
+		// Passwords match, so button should be enabled
+		assertEquals(createAccountButton.isEnabled(), true);
 	}
 
 	@Test
 	public void testCreateAccountUI() {
-		SetupController mockedController = this.setupController;
-		setupActivity.setController(mockedController);
-		// Mock strong password strength answer
-		when(mockedController.estimatePasswordStrength(anyString())).thenReturn(
-				STRONG);
+		proceedToPasswordFragment();
 		String safePass = "really.safe.password";
-		String nick = "nick.nickerton";
 		passwordEntry.setText(safePass);
 		passwordConfirmation.setText(safePass);
-		nicknameEntry.setText(nick);
 		// Confirm that the create account button is clickable
 		assertEquals(createAccountButton.isEnabled(), true);
 		createAccountButton.performClick();
-		// Verify that the controller's method was called with the correct
-		// params and get the callback
-		verify(mockedController, times(1))
-				.storeAuthorInfo(eq(nick), eq(safePass),
-						authorCaptor.capture());
-		authorCaptor.getValue().onResult(null);
+		// wait a second since there's no easy way to get a callback
+		try {
+			Thread.sleep(1000);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+		ShadowLooper.runUiThreadTasks();
 		// execute the callback
-		assertEquals(setupActivity.isFinishing(), true);
+		assertTrue(setupActivity.isFinishing());
 		// Confirm that the correct Activity has been started
 		ShadowActivity shadowActivity = shadowOf(setupActivity);
 		Intent intent = shadowActivity.peekNextStartedActivity();
+		assertNotNull(intent.getComponent());
 		assertEquals(intent.getComponent().getClassName(),
 				NavDrawerActivity.class.getName());
 	}
 
-	@Test
-	public void testNicknameUI() {
-		Assert.assertNotNull(setupActivity);
-		String longNick =
-				Strings.padEnd("*", AuthorConstants.MAX_AUTHOR_NAME_LENGTH + 1,
-						'*');
-		nicknameEntry.setText(longNick);
-		// Nickname should be too long
-		assertEquals(nicknameEntryWrapper.getError(),
-				setupActivity.getString(R.string.name_too_long));
-	}
-
 	@Test
 	public void testAccountCreation() {
 		SetupController controller = setupActivity.getController();
+		controller.setAuthorName("nick");
+		controller.setPassword("password");
 		// mock a resulthandler
-		ResultHandler<Void> resultHandler =
-				(ResultHandler<Void>) mock(ResultHandler.class);
-		controller.storeAuthorInfo("nick", "some.strong.pass", resultHandler);
-		// blocking verification call with timeout that waits until the mocked
-		// result gets called with handle 0L, the expected value
+		ResultHandler<Void> resultHandler = mock(ResultHandler.class);
+		controller.createAccount(resultHandler);
 		verify(resultHandler, timeout(TIMEOUT_MS).times(1)).onResult(null);
 		SharedPreferences prefs =
 				setupActivity.getSharedPreferences("db", Context.MODE_PRIVATE);
 		// Confirm database key
 		assertTrue(prefs.contains("key"));
-		// Note that Robolectric uses its own persistant storage that it
+		// Note that Robolectric uses its own persistent storage that it
 		// wipes clean after each test run, no need to clean up manually.
 	}
 
@@ -190,37 +159,37 @@ public class SetupActivityTest {
 
 	@Test
 	public void testStrengthMeterUI() {
-		Assert.assertNotNull(setupActivity);
-		// replace the setup controller with our mocked copy
-		SetupController mockedController = this.setupController;
-		setupActivity.setController(mockedController);
-		// Mock answers for UI testing only
-		when(mockedController.estimatePasswordStrength("strong")).thenReturn(
-				STRONG);
-		when(mockedController.estimatePasswordStrength("qstrong")).thenReturn(
-				QUITE_STRONG);
-		when(mockedController.estimatePasswordStrength("qweak")).thenReturn(
-				QUITE_WEAK);
-		when(mockedController.estimatePasswordStrength("weak")).thenReturn(
-				WEAK);
-		when(mockedController.estimatePasswordStrength("empty")).thenReturn(
-				NONE);
+		proceedToPasswordFragment();
 		// Test the meters progress and color for several values
-		testStrengthMeter("strong", STRONG, StrengthMeter.GREEN);
-		Mockito.verify(mockedController, Mockito.times(1))
-				.estimatePasswordStrength(eq("strong"));
-		testStrengthMeter("qstrong", QUITE_STRONG, StrengthMeter.LIME);
-		Mockito.verify(mockedController, Mockito.times(1))
-				.estimatePasswordStrength(eq("qstrong"));
-		testStrengthMeter("qweak", QUITE_WEAK, StrengthMeter.YELLOW);
-		Mockito.verify(mockedController, Mockito.times(1))
-				.estimatePasswordStrength(eq("qweak"));
-		testStrengthMeter("weak", WEAK, StrengthMeter.ORANGE);
-		Mockito.verify(mockedController, Mockito.times(1))
-				.estimatePasswordStrength(eq("weak"));
-		// Not sure this should be the correct behaviour on an empty input ?
-		testStrengthMeter("empty", NONE, StrengthMeter.RED);
-		Mockito.verify(mockedController, Mockito.times(1))
-				.estimatePasswordStrength(eq("empty"));
+		testStrengthMeter("1234567890ab", STRONG, StrengthMeter.GREEN);
+		testStrengthMeter("123456789", QUITE_STRONG, StrengthMeter.LIME);
+		testStrengthMeter("123456", QUITE_WEAK, StrengthMeter.YELLOW);
+		testStrengthMeter("123", WEAK, StrengthMeter.ORANGE);
+		testStrengthMeter("", NONE, StrengthMeter.RED);
 	}
+
+	private void proceedToPasswordFragment() {
+		// proceed to password fragment
+		nicknameEntry.setText("nick");
+		createAccountButton.performClick();
+
+		// find UI elements in new fragment
+		strengthMeter =
+				(StrengthMeter) setupActivity.findViewById(R.id.strength_meter);
+		passwordConfirmationWrapper = (TextInputLayout) setupActivity
+				.findViewById(R.id.password_confirm_wrapper);
+		passwordEntry =
+				(EditText) setupActivity.findViewById(R.id.password_entry);
+		passwordConfirmation =
+				(EditText) setupActivity.findViewById(R.id.password_confirm);
+		createAccountButton = (Button) setupActivity.findViewById(R.id.next);
+	}
+
+	private void testStrengthMeter(String pass, float strength, int color) {
+		passwordEntry.setText(pass);
+		assertEquals(strengthMeter.getProgress(),
+				(int) (strengthMeter.getMax() * strength));
+		assertEquals(color, strengthMeter.getColor());
+	}
+
 }
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java b/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java
index 3298c0e9be..ac4e60a88f 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java
@@ -10,15 +10,8 @@ public class TestChangePasswordActivity extends ChangePasswordActivity {
 		return passwordController;
 	}
 
-	public SetupController getSetupController() {
-		return setupController;
-	}
-
 	public void setPasswordController(PasswordController passwordController) {
 		this.passwordController = passwordController;
 	}
 
-	public void setSetupController(SetupController setupController) {
-		this.setupController = setupController;
-	}
 }
diff --git a/build.gradle b/build.gradle
index af816d45f5..80632d5ebf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,6 +4,9 @@ allprojects {
 	repositories {
 		jcenter()
 		mavenLocal()
+		maven {
+			url 'https://maven.google.com'
+		}
 	}
 }
 
-- 
GitLab