From f199105f6c2f6dd5867a88e82f936242a9a347af Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Thu, 30 Nov 2017 12:17:40 -0200
Subject: [PATCH] Add button for Huawei's power manager to setup wizard

---
 briar-android/build.gradle                    |   6 +-
 .../briar/android/login/DozeFragment.java     |  47 +++--
 .../briar/android/login/DozeView.java         |  60 +++++++
 .../briar/android/login/HuaweiView.java       |  72 ++++++++
 .../briar/android/login/PasswordFragment.java |   4 +-
 .../briar/android/login/PowerView.java        | 162 ++++++++++++++++++
 .../briar/android/login/SetupController.java  |   2 +-
 .../android/login/SetupControllerImpl.java    |   8 +-
 .../res/drawable/ic_help_outline_white.xml    |   9 +
 .../main/res/drawable/navigation_accept.xml   |  10 --
 .../res/layout/fragment_setup_author_name.xml |   2 +-
 .../main/res/layout/fragment_setup_doze.xml   |  38 ++--
 .../src/main/res/layout/power_view.xml        |  56 ++++++
 briar-android/src/main/res/values/color.xml   |   1 +
 briar-android/src/main/res/values/strings.xml |   7 +-
 briar-android/src/main/res/values/themes.xml  |   1 +
 .../android/login/PasswordFragmentTest.java   |   2 +-
 17 files changed, 437 insertions(+), 50 deletions(-)
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java
 create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java
 create mode 100644 briar-android/src/main/res/drawable/ic_help_outline_white.xml
 delete mode 100644 briar-android/src/main/res/drawable/navigation_accept.xml
 create mode 100644 briar-android/src/main/res/layout/power_view.xml

diff --git a/briar-android/build.gradle b/briar-android/build.gradle
index 889f4b06d9..0c52bcf581 100644
--- a/briar-android/build.gradle
+++ b/briar-android/build.gradle
@@ -20,7 +20,7 @@ dependencies {
 	}
 	implementation "com.android.support:cardview-v7:$supportVersion"
 	implementation "com.android.support:support-annotations:$supportVersion"
-	implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+	implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta3'
 
 	implementation('ch.acra:acra:4.8.5') {
 		exclude module: 'support-v4'
@@ -61,8 +61,8 @@ dependencyVerification {
 			'ch.acra:acra:4.8.5:acra-4.8.5.aar:afd5b28934d5166b55f261c85685ad59e8a4ebe9ca1960906afaa8c76d8dc9eb',
 			'classworlds:classworlds:1.1-alpha-2:classworlds-1.1-alpha-2.jar:2bf4e59f3acd106fea6145a9a88fe8956509f8b9c0fdd11eb96fee757269e3f3',
 			'com.almworks.sqlite4java:sqlite4java:0.282:sqlite4java-0.282.jar:9e1d8dd83ca6003f841e3af878ce2dc7c22497493a7bb6d1b62ec1b0d0a83c05',
-			'com.android.support.constraint:constraint-layout-solver:1.0.2:constraint-layout-solver-1.0.2.jar:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d',
-			'com.android.support.constraint:constraint-layout:1.0.2:constraint-layout-1.0.2.aar:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085',
+			'com.android.support.constraint:constraint-layout-solver:1.1.0-beta3:constraint-layout-solver-1.1.0-beta3.jar:c9084108415046c423983bdff8cf04c8e9a5bed41b8d5329f3764c08312ee3dd',
+			'com.android.support.constraint:constraint-layout:1.1.0-beta3:constraint-layout-1.1.0-beta3.aar:1754a6bd135feae485aa2ebf9e170f0f3d3282b392f8aa3067d0ed668839db79',
 			'com.android.support:animated-vector-drawable:27.0.1:animated-vector-drawable-27.0.1.aar:365050110411c86c7eec86101b49ab53557ffe6667f60b19055f1d35c38a577b',
 			'com.android.support:appcompat-v7:27.0.1:appcompat-v7-27.0.1.aar:1402c29a49db30346c21a7d40634461765b3ab826f5dd95bc4dcc76787b21851',
 			'com.android.support:cardview-v7:27.0.1:cardview-v7-27.0.1.aar:43fccd44086c51eaa9d78be2fcf0dfea1556c8876a6fd325ea8d24e860054202',
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
index cb77e3d9fa..f6362a2d89 100644
--- 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
@@ -1,17 +1,19 @@
 package org.briarproject.briar.android.login;
 
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.ProgressBar;
 
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
 import org.briarproject.briar.android.util.UiUtils;
 
 import static android.view.View.INVISIBLE;
@@ -19,12 +21,15 @@ 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 {
+@NotNullByDefault
+public class DozeFragment extends SetupFragment
+		implements OnCheckedChangedListener {
 
 	private final static String TAG = DozeFragment.class.getName();
 
-	private Button dozeButton;
+	private DozeView dozeView;
+	private HuaweiView huaweiView;
+	private Button next;
 	private ProgressBar progressBar;
 	private boolean secondAttempt = false;
 
@@ -33,15 +38,22 @@ public class DozeFragment extends SetupFragment {
 	}
 
 	@Override
-	public View onCreateView(LayoutInflater inflater, ViewGroup container,
-			Bundle savedInstanceState) {
+	public View onCreateView(LayoutInflater inflater,
+			@Nullable ViewGroup container,
+			@Nullable Bundle savedInstanceState) {
 		getActivity().setTitle(getString(R.string.setup_doze_title));
+		setHasOptionsMenu(false);
 		View v = inflater.inflate(R.layout.fragment_setup_doze, container,
 						false);
-		dozeButton = v.findViewById(R.id.dozeButton);
+		dozeView = v.findViewById(R.id.dozeView);
+		dozeView.setOnCheckedChangedListener(this);
+		huaweiView = v.findViewById(R.id.huaweiView);
+		huaweiView.setOnCheckedChangedListener(this);
+		next = v.findViewById(R.id.next);
 		progressBar = v.findViewById(R.id.progress);
 
-		dozeButton.setOnClickListener(view -> askForDozeWhitelisting());
+		dozeView.setOnButtonClickListener(this::askForDozeWhitelisting);
+		next.setOnClickListener(this);
 
 		return v;
 	}
@@ -65,25 +77,34 @@ public class DozeFragment extends SetupFragment {
 	public void onActivityResult(int request, int result, Intent data) {
 		super.onActivityResult(request, result, data);
 		if (request == REQUEST_DOZE_WHITELISTING) {
-			if (!setupController.needsDozeWhitelisting() || secondAttempt) {
-				dozeButton.setEnabled(false);
-				onClick(dozeButton);
-			} else {
+			if (!dozeView.needsToBeShown() || secondAttempt) {
+				dozeView.setChecked(true);
+			} else if (getContext() != null) {
 				secondAttempt = true;
 				showOnboardingDialog(getContext(), getHelpText());
 			}
 		}
 	}
 
+	@Override
+	public void onCheckedChanged() {
+		if (dozeView.isChecked() && huaweiView.isChecked()) {
+			next.setEnabled(true);
+		} else {
+			next.setEnabled(false);
+		}
+	}
+
 	@SuppressLint("BatteryLife")
 	private void askForDozeWhitelisting() {
+		if (getContext() == null) return;
 		Intent i = UiUtils.getDozeWhitelistingIntent(getContext());
 		startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
 	}
 
 	@Override
 	public void onClick(View view) {
-		dozeButton.setVisibility(INVISIBLE);
+		next.setVisibility(INVISIBLE);
 		progressBar.setVisibility(VISIBLE);
 		setupController.createAccount();
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java b/briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java
new file mode 100644
index 0000000000..a2b1c51862
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java
@@ -0,0 +1,60 @@
+package org.briarproject.briar.android.login;
+
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.util.AttributeSet;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.R;
+
+import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
+
+@UiThread
+@NotNullByDefault
+class DozeView extends PowerView {
+
+	@Nullable
+	private Runnable onButtonClickListener;
+
+	public DozeView(Context context) {
+		this(context, null);
+	}
+
+	public DozeView(Context context, @Nullable AttributeSet attrs) {
+		this(context, attrs, 0);
+	}
+
+	public DozeView(Context context, @Nullable AttributeSet attrs,
+			int defStyleAttr) {
+		super(context, attrs, defStyleAttr);
+		setText(R.string.setup_doze_intro);
+		setButtonText(R.string.setup_doze_button);
+	}
+
+	@Override
+	public boolean needsToBeShown() {
+		return needsToBeShown(getContext());
+	}
+
+	public static boolean needsToBeShown(Context context) {
+		return needsDozeWhitelisting(context);
+	}
+
+	@Override
+	protected int getHelpText() {
+		return R.string.setup_doze_explanation;
+	}
+
+	@Override
+	protected void onButtonClick() {
+		if (onButtonClickListener == null) throw new IllegalStateException();
+		onButtonClickListener.run();
+	}
+
+	public void setOnButtonClickListener(Runnable runnable) {
+		onButtonClickListener = runnable;
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java b/briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java
new file mode 100644
index 0000000000..dec5c913bb
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java
@@ -0,0 +1,72 @@
+package org.briarproject.briar.android.login;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.util.AttributeSet;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.R;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+@UiThread
+@NotNullByDefault
+class HuaweiView extends PowerView {
+
+	private final static String PACKAGE_NAME = "com.huawei.systemmanager";
+	private final static String CLASS_NAME =
+			PACKAGE_NAME + ".optimize.process.ProtectActivity";
+
+	public HuaweiView(Context context) {
+		this(context, null);
+	}
+
+	public HuaweiView(Context context, @Nullable AttributeSet attrs) {
+		this(context, attrs, 0);
+	}
+
+	public HuaweiView(Context context, @Nullable AttributeSet attrs,
+			int defStyleAttr) {
+		super(context, attrs, defStyleAttr);
+		setText(R.string.setup_huawei_text);
+		setButtonText(R.string.setup_huawei_button);
+	}
+
+	@Override
+	public boolean needsToBeShown() {
+		return needsToBeShown(getContext());
+	}
+
+	public static boolean needsToBeShown(Context context) {
+		PackageManager pm = context.getPackageManager();
+		List<ResolveInfo> resolveInfos = pm.queryIntentActivities(getIntent(),
+				PackageManager.MATCH_DEFAULT_ONLY);
+		return !resolveInfos.isEmpty();
+	}
+
+	@Override
+	@StringRes
+	protected int getHelpText() {
+		return R.string.setup_huawei_help;
+	}
+
+	@Override
+	protected void onButtonClick() {
+		getContext().startActivity(getIntent());
+		setChecked(true);
+	}
+
+	private static Intent getIntent() {
+		Intent intent = new Intent();
+		intent.setClassName(PACKAGE_NAME, CLASS_NAME);
+		return intent;
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
index 17f7e06eb1..80b8e04e41 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java
@@ -66,7 +66,7 @@ public class PasswordFragment extends SetupFragment {
 		component.inject(this);
 
 		// the controller is not yet available in onCreateView()
-		if (!setupController.needsDozeWhitelisting()) {
+		if (!setupController.needToShowDozeFragment()) {
 			nextButton.setText(R.string.create_account_button);
 		}
 	}
@@ -102,7 +102,7 @@ public class PasswordFragment extends SetupFragment {
 
 	@Override
 	public void onClick(View view) {
-		if (!setupController.needsDozeWhitelisting()) {
+		if (!setupController.needToShowDozeFragment()) {
 			nextButton.setVisibility(INVISIBLE);
 			progressBar.setVisibility(VISIBLE);
 		}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java
new file mode 100644
index 0000000000..a09ff23fc4
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java
@@ -0,0 +1,162 @@
+package org.briarproject.briar.android.login;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.support.constraint.ConstraintLayout;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.R;
+
+import static android.content.Context.LAYOUT_INFLATER_SERVICE;
+import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
+
+@UiThread
+@NotNullByDefault
+abstract class PowerView extends ConstraintLayout {
+
+	private final TextView textView;
+	private final ImageView checkImage;
+	private final Button button;
+
+	private boolean checked = false;
+
+	@Nullable
+	private OnCheckedChangedListener onCheckedChangedListener;
+
+	public PowerView(Context context) {
+		this(context, null);
+	}
+
+	public PowerView(Context context, @Nullable AttributeSet attrs) {
+		this(context, attrs, 0);
+	}
+
+	@SuppressWarnings("ConstantConditions")
+	public PowerView(Context context, @Nullable AttributeSet attrs,
+			int defStyleAttr) {
+		super(context, attrs, defStyleAttr);
+
+		LayoutInflater inflater = (LayoutInflater) context
+				.getSystemService(LAYOUT_INFLATER_SERVICE);
+		View v = inflater.inflate(R.layout.power_view, this, true);
+
+		textView = v.findViewById(R.id.textView);
+		checkImage = v.findViewById(R.id.checkImage);
+		button = v.findViewById(R.id.button);
+		button.setOnClickListener(view -> onButtonClick());
+		ImageButton helpButton = v.findViewById(R.id.helpButton);
+		helpButton.setOnClickListener(view -> onHelpButtonClick());
+
+		// we need to manage the checkImage state ourselves, because automatic
+		// state saving is done based on the view's ID and there can be
+		// multiple ImageViews with the same ID in the view hierarchy
+		setSaveFromParentEnabled(true);
+
+		if (!isInEditMode() && !needsToBeShown()) {
+			setVisibility(GONE);
+		}
+	}
+
+	@Nullable
+	@Override
+	protected Parcelable onSaveInstanceState() {
+		Parcelable superState = super.onSaveInstanceState();
+		SavedState ss = new SavedState(superState);
+		ss.value = new boolean[] {checked};
+		return ss;
+	}
+
+	@Override
+	protected void onRestoreInstanceState(Parcelable state) {
+		SavedState ss = (SavedState) state;
+		super.onRestoreInstanceState(ss.getSuperState());
+		setChecked(ss.value[0]);  // also calls listener
+	}
+
+	public abstract boolean needsToBeShown();
+
+	public void setChecked(boolean checked) {
+		this.checked = checked;
+		if (checked) {
+			checkImage.setImageResource(R.drawable.ic_check_white);
+		} else {
+			checkImage.setImageResource(R.drawable.contact_disconnected);
+		}
+		if (onCheckedChangedListener != null) {
+			onCheckedChangedListener.onCheckedChanged();
+		}
+	}
+
+	public boolean isChecked() {
+		return getVisibility() == GONE || checked;
+	}
+
+	public void setOnCheckedChangedListener(
+			OnCheckedChangedListener onCheckedChangedListener) {
+		this.onCheckedChangedListener = onCheckedChangedListener;
+	}
+
+	@StringRes
+	protected abstract int getHelpText();
+
+	protected void setText(@StringRes int res) {
+		textView.setText(res);
+	}
+
+	protected void setButtonText(@StringRes int res) {
+		button.setText(res);
+	}
+
+	protected abstract void onButtonClick();
+
+	private void onHelpButtonClick() {
+		showOnboardingDialog(getContext(),
+				getContext().getString(getHelpText()));
+	}
+
+	private static class SavedState extends BaseSavedState {
+		private boolean[] value = {false};
+
+		private SavedState(@Nullable Parcelable superState) {
+			super(superState);
+		}
+
+		private SavedState(Parcel in) {
+			super(in);
+			in.readBooleanArray(value);
+		}
+
+		@Override
+		public void writeToParcel(Parcel out, int flags) {
+			super.writeToParcel(out, flags);
+			out.writeBooleanArray(value);
+		}
+
+		static final Parcelable.Creator<SavedState> CREATOR
+				= new Parcelable.Creator<SavedState>() {
+			public SavedState createFromParcel(Parcel in) {
+				return new SavedState(in);
+			}
+
+			public SavedState[] newArray(int size) {
+				return new SavedState[size];
+			}
+		};
+	}
+
+	interface OnCheckedChangedListener {
+		void onCheckedChanged();
+	}
+
+}
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 cf75077fe5..870b64df0b 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
@@ -8,7 +8,7 @@ public interface SetupController {
 
 	void setSetupActivity(SetupActivity setupActivity);
 
-	boolean needsDozeWhitelisting();
+	boolean needToShowDozeFragment();
 
 	void setAuthorName(String authorName);
 
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 4d1f7ee739..00f2b012b8 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
@@ -11,7 +11,6 @@ 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;
 
@@ -41,9 +40,10 @@ public class SetupControllerImpl extends PasswordControllerImpl
 	}
 
 	@Override
-	public boolean needsDozeWhitelisting() {
+	public boolean needToShowDozeFragment() {
 		if (setupActivity == null) throw new IllegalStateException();
-		return UiUtils.needsDozeWhitelisting(setupActivity);
+		return DozeView.needsToBeShown(setupActivity) ||
+				HuaweiView.needsToBeShown(setupActivity);
 	}
 
 	@Override
@@ -61,7 +61,7 @@ public class SetupControllerImpl extends PasswordControllerImpl
 	@Override
 	public void showDozeOrCreateAccount() {
 		if (setupActivity == null) throw new IllegalStateException();
-		if (needsDozeWhitelisting()) {
+		if (needToShowDozeFragment()) {
 			setupActivity.showDozeFragment();
 		} else {
 			createAccount();
diff --git a/briar-android/src/main/res/drawable/ic_help_outline_white.xml b/briar-android/src/main/res/drawable/ic_help_outline_white.xml
new file mode 100644
index 0000000000..b39381493b
--- /dev/null
+++ b/briar-android/src/main/res/drawable/ic_help_outline_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0">
+	<path
+		android:fillColor="#FFFFFFFF"
+		android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/briar-android/src/main/res/drawable/navigation_accept.xml b/briar-android/src/main/res/drawable/navigation_accept.xml
deleted file mode 100644
index 8dcd06dd6d..0000000000
--- a/briar-android/src/main/res/drawable/navigation_accept.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:alpha="0.54"
-        android:viewportHeight="24.0"
-        android:viewportWidth="24.0">
-	<path
-		android:fillColor="#FF000000"
-		android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
-</vector>
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
index 8f22bc7edf..eae7ece48b 100644
--- a/briar-android/src/main/res/layout/fragment_setup_author_name.xml
+++ b/briar-android/src/main/res/layout/fragment_setup_author_name.xml
@@ -26,7 +26,7 @@
 			app:hintEnabled="false"
 			app:layout_constraintLeft_toLeftOf="parent"
 			app:layout_constraintRight_toRightOf="parent"
-			app:layout_constraintTop_toBottomOf="parent">
+			app:layout_constraintTop_toTopOf="parent">
 
 			<android.support.design.widget.TextInputEditText
 				android:id="@+id/nickname_entry"
diff --git a/briar-android/src/main/res/layout/fragment_setup_doze.xml b/briar-android/src/main/res/layout/fragment_setup_doze.xml
index fedb98f6db..787a8166ba 100644
--- a/briar-android/src/main/res/layout/fragment_setup_doze.xml
+++ b/briar-android/src/main/res/layout/fragment_setup_doze.xml
@@ -2,6 +2,7 @@
 <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">
@@ -16,38 +17,47 @@
 		android:paddingStart="@dimen/margin_activity_horizontal"
 		android:paddingTop="@dimen/margin_activity_vertical">
 
-		<TextView
-			android:id="@+id/setup_explanation"
+		<org.briarproject.briar.android.login.DozeView
+			android:id="@+id/dozeView"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:text="@string/setup_doze_intro"
-			android:textSize="@dimen/text_size_medium"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
 			app:layout_constraintStart_toStartOf="parent"
 			app:layout_constraintTop_toTopOf="parent"/>
 
+		<org.briarproject.briar.android.login.HuaweiView
+			android:id="@+id/huaweiView"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/dozeView"/>
+
 		<Button
-			android:id="@+id/dozeButton"
+			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:text="@string/setup_doze_button"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toBottomOf="@+id/setup_explanation"/>
+			android:enabled="false"
+			android:text="@string/create_account_button"
+			app:layout_constraintBottom_toBottomOf="parent"
+			app:layout_constraintLeft_toLeftOf="parent"
+			app:layout_constraintRight_toRightOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/huaweiView"
+			app:layout_constraintVertical_bias="1.0"
+			tools:enabled="true"/>
 
 		<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_constraintBottom_toBottomOf="@+id/next"
 			app:layout_constraintLeft_toLeftOf="parent"
 			app:layout_constraintRight_toRightOf="parent"
-			app:layout_constraintTop_toTopOf="@+id/dozeButton"/>
+			app:layout_constraintTop_toTopOf="@+id/next"/>
 
 	</android.support.constraint.ConstraintLayout>
 
diff --git a/briar-android/src/main/res/layout/power_view.xml b/briar-android/src/main/res/layout/power_view.xml
new file mode 100644
index 0000000000..643825f607
--- /dev/null
+++ b/briar-android/src/main/res/layout/power_view.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge
+	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="wrap_content"
+	tools:parentTag="android.support.constraint.ConstraintLayout">
+
+	<TextView
+		android:id="@+id/textView"
+		android:layout_width="0dp"
+		android:layout_height="wrap_content"
+		android:layout_marginTop="8dp"
+		android:textSize="@dimen/text_size_medium"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toTopOf="parent"
+		tools:text="@string/setup_huawei_text"/>
+
+	<ImageView
+		android:id="@+id/checkImage"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:src="@drawable/contact_disconnected"
+		android:tint="?colorControlNormal"
+		app:layout_constraintBottom_toBottomOf="@+id/button"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toTopOf="@+id/button"
+		tools:ignore="ContentDescription"/>
+
+	<Button
+		android:id="@+id/button"
+		style="@style/BriarButton.Default"
+		android:layout_width="0dp"
+		android:layout_height="wrap_content"
+		android:layout_marginEnd="8dp"
+		android:layout_marginStart="8dp"
+		android:layout_marginTop="8dp"
+		app:layout_constraintEnd_toStartOf="@+id/helpButton"
+		app:layout_constraintStart_toEndOf="@+id/checkImage"
+		app:layout_constraintTop_toBottomOf="@+id/textView"
+		tools:text="@string/setup_huawei_button"/>
+
+	<ImageButton
+		android:id="@+id/helpButton"
+		style="@style/BriarButton.Default"
+		android:layout_width="48dp"
+		android:layout_height="wrap_content"
+		android:contentDescription="@string/help"
+		android:src="@drawable/ic_help_outline_white"
+		app:layout_constraintBottom_toBottomOf="@+id/button"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintTop_toTopOf="@+id/button"/>
+
+</merge>
\ No newline at end of file
diff --git a/briar-android/src/main/res/values/color.xml b/briar-android/src/main/res/values/color.xml
index 3110965f78..1f067ff84e 100644
--- a/briar-android/src/main/res/values/color.xml
+++ b/briar-android/src/main/res/values/color.xml
@@ -16,6 +16,7 @@
 	<color name="briar_primary_dark">@color/briar_blue_dark</color>
 
 	<color name="briar_accent">@color/briar_blue</color>
+	<color name="control_normal_light">#757575</color>
 
 	<!-- text colors -->
 	<color name="briar_text_link">#06b9ff</color>
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index 3393d6889d..97da612ac0 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -21,6 +21,11 @@
 	<string name="more_info">More Information</string>
 	<string name="don_t_ask_again">Don\'t ask again</string>
 
+	<string name="setup_huawei_text">Please tap the button below and make sure Briar is protected in the \"Protected Apps\" screen.</string>
+	<string name="setup_huawei_button">Protect Briar</string>
+	<string name="setup_huawei_help">If Briar is not added to the protected apps list, it will be unable to run in the background.</string>
+	<string name="warning_dozed">%s was unable to run in the background</string>
+
 	<!-- Login -->
 	<string name="enter_password">Enter your password:</string>
 	<string name="try_again">Wrong password, try again</string>
@@ -95,8 +100,8 @@
 	<string name="ellipsis">…</string>
 	<string name="text_too_long">The entered text is too long</string>
 	<string name="show_onboarding">Show Help Dialog</string>
-	<string name="warning_dozed">%s was unable to run in the background</string>
 	<string name="fix">Fix</string>
+	<string name="help">Help</string>
 
 	<!-- Contacts and Private Conversations-->
 	<string name="no_contacts">It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.</string>
diff --git a/briar-android/src/main/res/values/themes.xml b/briar-android/src/main/res/values/themes.xml
index a9b20148cc..700b3b6ffd 100644
--- a/briar-android/src/main/res/values/themes.xml
+++ b/briar-android/src/main/res/values/themes.xml
@@ -5,6 +5,7 @@
 		<item name="colorPrimary">@color/briar_primary</item>
 		<item name="colorPrimaryDark">@color/briar_primary_dark</item>
 		<item name="colorAccent">@color/briar_accent</item>
+		<item name="colorControlNormal">@color/control_normal_light</item>
 		<item name="android:textColorLink">@color/briar_text_link</item>
 		<item name="android:windowBackground">@color/window_background</item>
 		<item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java
index 490f2653a8..a503e44226 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java
@@ -61,7 +61,7 @@ public class PasswordFragmentTest {
 		String safePass = "really.safe.password";
 
 		passwordFragment.setupController = setupController;
-		when(setupController.needsDozeWhitelisting()).thenReturn(false);
+		when(setupController.needToShowDozeFragment()).thenReturn(false);
 		when(setupController.estimatePasswordStrength(safePass))
 				.thenReturn(STRONG);
 
-- 
GitLab