From cc33c635014bef0dc38b788ddbc8003dc088de92 Mon Sep 17 00:00:00 2001 From: orangenbaumblatt Date: Tue, 16 Jun 2020 22:50:17 +0200 Subject: [PATCH] Add password strength meter String refactor Layout improvements Layout improvements Fixes after rebuild --- app/build.gradle | 1 + .../aegis/helpers/PasswordStrengthHelper.java | 27 ++++++++ .../com/beemdevelopment/aegis/ui/Dialogs.java | 21 ++++++ .../aegis/ui/slides/SecuritySetupSlide.java | 37 +++++++++++ app/src/main/res/layout/dialog_password.xml | 59 +++++++++++++---- .../layout/fragment_security_setup_slide.xml | 65 ++++++++++--------- app/src/main/res/values/arrays.xml | 8 +++ app/src/main/res/values/strings.xml | 6 ++ 8 files changed, 182 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java diff --git a/app/build.gradle b/app/build.gradle index c2d8078b..36516842 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,6 +122,7 @@ dependencies { implementation 'com.google.protobuf:protobuf-javalite:3.12.1' implementation "com.mikepenz:iconics-core:3.2.5" implementation 'com.mikepenz:material-design-iconic-typeface:2.2.0.5@aar' + implementation 'com.nulab-inc:zxcvbn:1.3.0' implementation 'de.hdodenhof:circleimageview:3.1.0' implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0' implementation 'me.dm7.barcodescanner:zxing:1.9.8' diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java new file mode 100644 index 00000000..518ebcc1 --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/PasswordStrengthHelper.java @@ -0,0 +1,27 @@ +package com.beemdevelopment.aegis.helpers; + +import android.content.Context; + +import com.beemdevelopment.aegis.R; + +public class PasswordStrengthHelper { + // Material design color palette + private static String[] COLORS = {"#FF5252", "#FF5252", "#FFC107", "#8BC34A", "#4CAF50"}; + + public static String getString(int score, Context context) { + if (score < 0 || score > 4) { + throw new IllegalArgumentException("Not a valid zxcvbn score"); + } + + String[] strings = context.getResources().getStringArray(R.array.password_strength); + return strings[score]; + } + + public static String getColor(int score) { + if (score < 0 || score > 4) { + throw new IllegalArgumentException("Not a valid zxcvbn score"); + } + + return COLORS[score]; + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java b/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java index 16978e85..0fc0e827 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java @@ -7,6 +7,8 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; import android.provider.Settings; import android.text.Editable; import android.text.InputType; @@ -20,6 +22,7 @@ import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.NumberPicker; +import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.StringRes; @@ -28,10 +31,14 @@ import androidx.appcompat.app.AlertDialog; import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.helpers.EditTextHelper; +import com.beemdevelopment.aegis.helpers.PasswordStrengthHelper; import com.beemdevelopment.aegis.ui.tasks.KeyDerivationTask; import com.beemdevelopment.aegis.vault.slots.PasswordSlot; import com.beemdevelopment.aegis.vault.slots.Slot; import com.beemdevelopment.aegis.vault.slots.SlotException; +import com.google.android.material.textfield.TextInputLayout; +import com.nulabinc.zxcvbn.Strength; +import com.nulabinc.zxcvbn.Zxcvbn; import java.util.concurrent.atomic.AtomicReference; @@ -90,9 +97,13 @@ public class Dialogs { } public static void showSetPasswordDialog(Activity activity, Dialogs.SlotListener listener) { + Zxcvbn zxcvbn = new Zxcvbn(); View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null); EditText textPassword = view.findViewById(R.id.text_password); EditText textPasswordConfirm = view.findViewById(R.id.text_password_confirm); + ProgressBar barPasswordStrength = view.findViewById(R.id.progressBar); + TextView textPasswordStrength = view.findViewById(R.id.text_password_strength); + TextInputLayout textPasswordWrapper = view.findViewById(R.id.text_password_wrapper); CheckBox switchToggleVisibility = view.findViewById(R.id.check_toggle_visibility); switchToggleVisibility.setOnCheckedChangeListener((CompoundButton.OnCheckedChangeListener) (buttonView, isChecked) -> { @@ -145,14 +156,24 @@ public class Dialogs { }); TextWatcher watcher = new TextWatcher() { + @Override public void onTextChanged(CharSequence c, int start, int before, int count) { boolean equal = EditTextHelper.areEditTextsEqual(textPassword, textPasswordConfirm); buttonOK.get().setEnabled(equal); + + Strength strength = zxcvbn.measure(textPassword.getText()); + barPasswordStrength.setProgress(strength.getScore()); + barPasswordStrength.setProgressTintList(ColorStateList.valueOf(Color.parseColor(PasswordStrengthHelper.getColor(strength.getScore())))); + textPasswordStrength.setText((textPassword.getText().length() != 0) ? PasswordStrengthHelper.getString(strength.getScore(), activity) : ""); + textPasswordWrapper.setError(strength.getFeedback().getWarning()); + strength.wipe(); } + @Override public void beforeTextChanged(CharSequence c, int start, int count, int after) { } + @Override public void afterTextChanged(Editable c) { } }; diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java index e9984f73..02dad8a0 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/SecuritySetupSlide.java @@ -1,13 +1,19 @@ package com.beemdevelopment.aegis.ui.slides; import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.biometric.BiometricPrompt; @@ -17,6 +23,7 @@ import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer; import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.beemdevelopment.aegis.helpers.EditTextHelper; +import com.beemdevelopment.aegis.helpers.PasswordStrengthHelper; import com.beemdevelopment.aegis.ui.Dialogs; import com.beemdevelopment.aegis.ui.IntroActivity; import com.beemdevelopment.aegis.ui.tasks.KeyDerivationTask; @@ -28,6 +35,9 @@ import com.beemdevelopment.aegis.vault.slots.SlotException; import com.github.appintro.SlidePolicy; import com.github.appintro.SlideSelectionListener; import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.textfield.TextInputLayout; +import com.nulabinc.zxcvbn.Strength; +import com.nulabinc.zxcvbn.Zxcvbn; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -37,16 +47,23 @@ public class SecuritySetupSlide extends Fragment implements SlidePolicy, SlideSe private EditText _textPassword; private EditText _textPasswordConfirm; private CheckBox _checkPasswordVisibility; + private ProgressBar _barPasswordStrength; + private TextView _textPasswordStrength; + private TextInputLayout _textPasswordWrapper; private int _cryptType; private VaultFileCredentials _creds; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + Zxcvbn zxcvbn = new Zxcvbn(); final View view = inflater.inflate(R.layout.fragment_security_setup_slide, container, false); _textPassword = view.findViewById(R.id.text_password); _textPasswordConfirm = view.findViewById(R.id.text_password_confirm); _checkPasswordVisibility = view.findViewById(R.id.check_toggle_visibility); + _barPasswordStrength = view.findViewById(R.id.progressBar); + _textPasswordStrength = view.findViewById(R.id.text_password_strength); + _textPasswordWrapper = view.findViewById(R.id.text_password_wrapper); _checkPasswordVisibility.setOnCheckedChangeListener((buttonView, isChecked) -> { if (isChecked) { @@ -60,6 +77,26 @@ public class SecuritySetupSlide extends Fragment implements SlidePolicy, SlideSe } }); + _textPassword.addTextChangedListener(new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + Strength strength = zxcvbn.measure(_textPassword.getText()); + _barPasswordStrength.setProgress(strength.getScore()); + _barPasswordStrength.setProgressTintList(ColorStateList.valueOf(Color.parseColor(PasswordStrengthHelper.getColor(strength.getScore())))); + _textPasswordStrength.setText((_textPassword.getText().length() != 0) ? PasswordStrengthHelper.getString(strength.getScore(), getContext()) : ""); + _textPasswordWrapper.setError(strength.getFeedback().getWarning()); + strength.wipe(); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + view.findViewById(R.id.main).setBackgroundColor(_bgColor); return view; } diff --git a/app/src/main/res/layout/dialog_password.xml b/app/src/main/res/layout/dialog_password.xml index d73d74ee..4655c3bb 100644 --- a/app/src/main/res/layout/dialog_password.xml +++ b/app/src/main/res/layout/dialog_password.xml @@ -14,19 +14,50 @@ android:textColor="#FF0000" android:textStyle="bold" android:layout_marginStart="5dp" - android:layout_marginEnd="5dp"/> - + + - + + + + + + android:layout_height="wrap_content" + android:layout_marginTop="10dp"> + + + + + + + + diff --git a/app/src/main/res/layout/fragment_security_setup_slide.xml b/app/src/main/res/layout/fragment_security_setup_slide.xml index 2af04750..f229d063 100644 --- a/app/src/main/res/layout/fragment_security_setup_slide.xml +++ b/app/src/main/res/layout/fragment_security_setup_slide.xml @@ -28,44 +28,47 @@ android:layout_height="wrap_content" android:layout_marginTop="24dp"> - + android:layout_height="wrap_content"> - + + + + + + + + - + android:max="4" + android:paddingStart="4dp" + android:paddingEnd="3.5dp" /> - - + android:layout_gravity="end" /> tr + + @string/password_strength_very_weak + @string/password_strength_weak + @string/password_strength_fair + @string/password_strength_good + @string/password_strength_strong + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db8ff01c..fa58d4c6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -276,4 +276,10 @@ Previous Transfer entry Scan this QR code with the authenticator app you would like to transfer this entry to + + Very weak + Weak + Fair + Good + Strong