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