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