diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java
index b35edac..fd6da0b 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java
@@ -50,9 +50,8 @@ import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.util.ledger.Monero;
+import com.m2049r.xmrwallet.widget.PasswordEntryView;
import com.m2049r.xmrwallet.widget.Toolbar;
-import com.nulabinc.zxcvbn.Strength;
-import com.nulabinc.zxcvbn.Zxcvbn;
import java.io.File;
import java.text.ParseException;
@@ -70,7 +69,7 @@ public class GenerateFragment extends Fragment {
static final String TYPE_VIEWONLY = "view";
private TextInputLayout etWalletName;
- private TextInputLayout etWalletPassword;
+ private PasswordEntryView etWalletPassword;
private LinearLayout llFingerprintAuth;
private TextInputLayout etWalletAddress;
private TextInputLayout etWalletMnemonic;
@@ -131,21 +130,6 @@ public class GenerateFragment extends Fragment {
});
clearErrorOnTextEntry(etWalletName);
- etWalletPassword.getEditText().addTextChangedListener(new TextWatcher() {
- @Override
- public void afterTextChanged(Editable editable) {
- checkPassword();
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
- });
-
etWalletMnemonic.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
@@ -354,45 +338,10 @@ public class GenerateFragment extends Fragment {
});
etWalletName.requestFocus();
- initZxcvbn();
return view;
}
- Zxcvbn zxcvbn = new Zxcvbn();
-
- // initialize zxcvbn engine in background thread
- private void initZxcvbn() {
- new Thread(new Runnable() {
- @Override
- public void run() {
- zxcvbn.measure("");
- }
- }).start();
- }
-
- private void checkPassword() {
- String password = etWalletPassword.getEditText().getText().toString();
- if (!password.isEmpty()) {
- Strength strength = zxcvbn.measure(password);
- int msg;
- double guessesLog10 = strength.getGuessesLog10();
- if (guessesLog10 < 10)
- msg = R.string.password_weak;
- else if (guessesLog10 < 11)
- msg = R.string.password_fair;
- else if (guessesLog10 < 12)
- msg = R.string.password_good;
- else if (guessesLog10 < 13)
- msg = R.string.password_strong;
- else
- msg = R.string.password_very_strong;
- etWalletPassword.setError(getResources().getString(msg));
- } else {
- etWalletPassword.setError(null);
- }
- }
-
private boolean checkName() {
String name = etWalletName.getEditText().getText().toString();
boolean ok = true;
@@ -654,24 +603,7 @@ public class GenerateFragment extends Fragment {
final TextInputLayout etSeed = promptsView.findViewById(R.id.etSeed);
final TextInputLayout etPassphrase = promptsView.findViewById(R.id.etPassphrase);
- etSeed.getEditText().addTextChangedListener(new TextWatcher() {
- @Override
- public void afterTextChanged(Editable s) {
- if (etSeed.getError() != null) {
- etSeed.setError(null);
- }
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start,
- int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start,
- int before, int count) {
- }
- });
+ clearErrorOnTextEntry(etSeed);
alertDialogBuilder
.setCancelable(false)
diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java
index d65d8c1..310e318 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java
@@ -56,6 +56,7 @@ import com.m2049r.xmrwallet.util.FingerprintHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
+import com.m2049r.xmrwallet.widget.PasswordEntryView;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
@@ -473,7 +474,7 @@ public class GenerateReviewFragment extends Fragment {
AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(getActivity());
alertDialogBuilder.setView(promptsView);
- final TextInputLayout etPasswordA = promptsView.findViewById(R.id.etWalletPasswordA);
+ final PasswordEntryView etPasswordA = promptsView.findViewById(R.id.etWalletPasswordA);
etPasswordA.setHint(getString(R.string.prompt_changepw, walletName));
final TextInputLayout etPasswordB = promptsView.findViewById(R.id.etWalletPasswordB);
@@ -509,9 +510,6 @@ public class GenerateReviewFragment extends Fragment {
etPasswordA.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
- if (etPasswordA.getError() != null) {
- etPasswordA.setError(null);
- }
if (etPasswordB.getError() != null) {
etPasswordB.setError(null);
}
@@ -531,9 +529,6 @@ public class GenerateReviewFragment extends Fragment {
etPasswordB.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
- if (etPasswordA.getError() != null) {
- etPasswordA.setError(null);
- }
if (etPasswordB.getError() != null) {
etPasswordB.setError(null);
}
@@ -564,29 +559,20 @@ public class GenerateReviewFragment extends Fragment {
});
openDialog = alertDialogBuilder.create();
- openDialog.setOnShowListener(new DialogInterface.OnShowListener() {
- @Override
- public void onShow(final DialogInterface dialog) {
- Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- String newPasswordA = etPasswordA.getEditText().getText().toString();
- String newPasswordB = etPasswordB.getEditText().getText().toString();
- // disallow empty passwords
- if (newPasswordA.isEmpty()) {
- etPasswordA.setError(getString(R.string.generate_empty_passwordB));
- } else if (!newPasswordA.equals(newPasswordB)) {
- etPasswordB.setError(getString(R.string.generate_bad_passwordB));
- } else {
- new AsyncChangePassword().execute(newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
- Helper.hideKeyboardAlways(getActivity());
- openDialog.dismiss();
- openDialog = null;
- }
- }
- });
- }
+ openDialog.setOnShowListener(dialog -> {
+ Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
+ button.setOnClickListener(view -> {
+ String newPasswordA = etPasswordA.getEditText().getText().toString();
+ String newPasswordB = etPasswordB.getEditText().getText().toString();
+ if (!newPasswordA.equals(newPasswordB)) {
+ etPasswordB.setError(getString(R.string.generate_bad_passwordB));
+ } else {
+ new AsyncChangePassword().execute(newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
+ Helper.hideKeyboardAlways(getActivity());
+ openDialog.dismiss();
+ openDialog = null;
+ }
+ });
});
// accept keyboard "ok"
@@ -597,9 +583,7 @@ public class GenerateReviewFragment extends Fragment {
String newPasswordA = etPasswordA.getEditText().getText().toString();
String newPasswordB = etPasswordB.getEditText().getText().toString();
// disallow empty passwords
- if (newPasswordA.isEmpty()) {
- etPasswordA.setError(getString(R.string.generate_empty_passwordB));
- } else if (!newPasswordA.equals(newPasswordB)) {
+ if (!newPasswordA.equals(newPasswordB)) {
etPasswordB.setError(getString(R.string.generate_bad_passwordB));
} else {
new AsyncChangePassword().execute(newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java
index 66a3704..b5070dc 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java
@@ -59,9 +59,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.R;
-import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
-import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
import java.io.File;
import java.io.IOException;
@@ -77,7 +75,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.HttpsURLConnection;
-import okhttp3.HttpUrl;
import timber.log.Timber;
public class Helper {
diff --git a/app/src/main/java/com/m2049r/xmrwallet/widget/PasswordEntryView.java b/app/src/main/java/com/m2049r/xmrwallet/widget/PasswordEntryView.java
new file mode 100644
index 0000000..6d5fee1
--- /dev/null
+++ b/app/src/main/java/com/m2049r/xmrwallet/widget/PasswordEntryView.java
@@ -0,0 +1,73 @@
+package com.m2049r.xmrwallet.widget;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.material.textfield.TextInputLayout;
+import com.m2049r.xmrwallet.R;
+import com.nulabinc.zxcvbn.Zxcvbn;
+
+public class PasswordEntryView extends TextInputLayout implements TextWatcher {
+ final private Zxcvbn zxcvbn = new Zxcvbn();
+
+ public PasswordEntryView(@NonNull Context context) {
+ super(context, null);
+ }
+
+ public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs, R.attr.textInputStyle);
+ }
+
+ public PasswordEntryView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void addView(@NonNull View child, int index, @NonNull final ViewGroup.LayoutParams params) {
+ super.addView(child, index, params);
+ final EditText et = getEditText();
+ if (et != null)
+ et.addTextChangedListener(this);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ String password = s.toString();
+ int icon = 0;
+ if (!password.isEmpty()) {
+ final double strength = Math.min(zxcvbn.measure(password).getGuessesLog10(), 15) / 3 * 20; // 0-100%
+ if (strength < 21)
+ icon = R.drawable.ic_smiley_sad_filled;
+ else if (strength < 40)
+ icon = R.drawable.ic_smiley_meh_filled;
+ else if (strength < 60)
+ icon = R.drawable.ic_smiley_neutral_filled;
+ else if (strength < 80)
+ icon = R.drawable.ic_smiley_happy_filled;
+ else if (strength < 99)
+ icon = R.drawable.ic_smiley_ecstatic_filled;
+ else
+ icon = R.drawable.ic_smiley_gunther_filled;
+ }
+ setErrorIconDrawable(icon);
+ if (icon != 0)
+ setError(" ");
+ else setError(null);
+ }
+}
diff --git a/app/src/main/res/drawable/ic_smiley_ecstatic_filled.xml b/app/src/main/res/drawable/ic_smiley_ecstatic_filled.xml
new file mode 100644
index 0000000..5599f40
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smiley_ecstatic_filled.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_smiley_gunther_filled.xml b/app/src/main/res/drawable/ic_smiley_gunther_filled.xml
new file mode 100644
index 0000000..b984c88
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smiley_gunther_filled.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_smiley_happy_filled.xml b/app/src/main/res/drawable/ic_smiley_happy_filled.xml
new file mode 100644
index 0000000..2a90da4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smiley_happy_filled.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_smiley_meh_filled.xml b/app/src/main/res/drawable/ic_smiley_meh_filled.xml
new file mode 100644
index 0000000..057e41e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smiley_meh_filled.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_smiley_neutral_filled.xml b/app/src/main/res/drawable/ic_smiley_neutral_filled.xml
new file mode 100644
index 0000000..13cff5b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smiley_neutral_filled.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_smiley_sad_filled.xml b/app/src/main/res/drawable/ic_smiley_sad_filled.xml
new file mode 100644
index 0000000..db30f3e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smiley_sad_filled.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_generate.xml b/app/src/main/res/layout/fragment_generate.xml
index a3fa20b..20dbad7 100644
--- a/app/src/main/res/layout/fragment_generate.xml
+++ b/app/src/main/res/layout/fragment_generate.xml
@@ -29,13 +29,17 @@
android:textAlignment="textStart" />
-
+ app:boxStrokeErrorColor="@color/monerujoPrimary"
+ app:errorEnabled="true"
+ app:errorIconDrawable="@drawable/ic_smiley_gunther_filled"
+ app:errorIconTint="@color/monerujoPrimary"
+ app:errorTextColor="@color/monerujoPrimary">
-
+
-
+ app:boxStrokeErrorColor="@color/monerujoPrimary"
+ app:errorEnabled="true"
+ app:errorIconDrawable="@drawable/ic_smiley_gunther_filled"
+ app:errorIconTint="@color/monerujoPrimary"
+ app:errorTextColor="@color/monerujoPrimary">
-
+