mirror of https://github.com/m2049r/xmrwallet.git
show passphrase strength with icons (#722)
This commit is contained in:
parent
86d5d2a2cb
commit
f7a762f32b
|
@ -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)
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="85.43"
|
||||
android:viewportHeight="85.43">
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M43.37,4.22a38.5,38.5 0,1 0,38.5 38.49A38.54,38.54 0,0 0,43.37 4.22ZM17.51,36.9c0,-3.91 2.77,-9.74 10.42,-9.74S38.35,33 38.35,36.9a3.5,3.5 0,0 1,-7 0c-0.06,-1.8 -1.22,-2.74 -3.42,-2.74 -3.09,0 -3.42,1.92 -3.42,2.74a3.5,3.5 0,0 1,-7 0ZM65,50.11c-1.14,5.82 -7.12,16.83 -21.59,16.83s-20.45,-11 -21.59,-16.83a3.5,3.5 0,0 1,3.44 -4.17L61.53,45.94a3.5,3.5 0,0 1,2.7 1.27A3.46,3.46 0,0 1,65 50.11ZM65.78,40.4a3.5,3.5 0,0 1,-3.5 -3.5c-0.07,-1.8 -1.22,-2.74 -3.42,-2.74 -3.09,0 -3.43,1.92 -3.43,2.74a3.5,3.5 0,0 1,-7 0c0,-3.91 2.78,-9.74 10.43,-9.74S69.24,33 69.24,36.9A3.5,3.5 0,0 1,65.74 40.4Z" />
|
||||
</vector>
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="85.43"
|
||||
android:viewportHeight="85.43">
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M77.39,26A38.49,38.49 0,0 0,8 26Z" />
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M79.54,33c-2.3,8.19 -9.06,14.12 -17.06,14.12S47.73,41.19 45.43,33L41.34,33C39,41.19 31.93,47.12 23.63,47.12S8.31,41.19 5.92,33L5.46,33A38.5,38.5 0,1 0,80 33ZM34.65,63.54L12.24,63.54C20,51.84 29.09,49 32.86,48.34a8.44,8.44 0,0 1,1.6 -0.21h0.19a7.71,7.71 0,0 1,0 15.42ZM50.42,63.54a7.71,7.71 0,0 1,0 -15.42h0.19a8.34,8.34 0,0 1,1.6 0.21c3.78,0.68 12.87,3.5 20.62,15.2Z" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="85.43"
|
||||
android:viewportHeight="85.43">
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M42.8,4.22A38.5,38.5 0,1 0,81.29 42.71,38.54 38.54,0 0,0 42.8,4.22ZM58.21,28.5a5.29,5.29 0,1 1,-5.28 5.28A5.28,5.28 0,0 1,58.21 28.5ZM27.39,28.5a5.29,5.29 0,1 1,-5.29 5.28A5.28,5.28 0,0 1,27.39 28.5ZM64.39,50.11c-1.14,5.82 -7.12,16.83 -21.59,16.83s-20.45,-11 -21.59,-16.83a3.5,3.5 0,1 1,6.87 -1.34c0.23,1.12 2.68,11.17 14.72,11.17 12.39,0 14.63,-10.72 14.72,-11.17a3.5,3.5 0,1 1,6.87 1.34Z" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="85.43"
|
||||
android:viewportHeight="85.43">
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M42.71,4.22a38.5,38.5 0,1 0,38.5 38.49A38.54,38.54 0,0 0,42.71 4.22ZM22,33.78a5.28,5.28 0,1 1,5.28 5.29A5.27,5.27 0,0 1,22 33.78ZM58.83,53.78L28,60.24a3.5,3.5 0,0 1,-1.44 -6.85L57.4,46.9a3.5,3.5 0,0 1,1.45 6.85ZM58.11,39.1a5.29,5.29 0,1 1,5.28 -5.29A5.29,5.29 0,0 1,58.13 39.07Z" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="85.43"
|
||||
android:viewportHeight="85.43">
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M42.84,4.22a38.5,38.5 0,1 0,38.5 38.49A38.54,38.54 0,0 0,42.84 4.22ZM22.15,33.78a5.28,5.28 0,1 1,5.28 5.29A5.27,5.27 0,0 1,22.15 33.78ZM58.26,56.84L27.43,56.84a3.5,3.5 0,0 1,0 -7L58.26,49.84a3.5,3.5 0,0 1,0 7ZM58.26,39.07a5.29,5.29 0,1 1,5.28 -5.29A5.29,5.29 0,0 1,58.26 39.07Z" />
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="85.43"
|
||||
android:viewportHeight="85.43">
|
||||
<path
|
||||
android:fillColor="@color/monerujoPrimary"
|
||||
android:pathData="M44.89,4.22A38.5,38.5 0,1 0,83.38 42.71,38.54 38.54,0 0,0 44.89,4.22ZM19,30.66a3.5,3.5 0,0 1,7 0c0.07,1.8 1.22,2.74 3.42,2.74 3.09,0 3.43,-1.91 3.43,-2.74a3.5,3.5 0,0 1,7 0c0,3.92 -2.78,9.74 -10.43,9.74S19,34.58 19,30.66ZM65.31,60.29a3.44,3.44 0,0 1,-2 0.65,3.49 3.49,0 0,1 -2.86,-1.47 18.77,18.77 0,0 0,-31.09 0,3.5 3.5,0 0,1 -5.71,-4.06A26.25,26.25 0,0 1,44.89 44.22,26.25 26.25,0 0,1 66.14,55.41 3.5,3.5 0,0 1,65.31 60.29ZM60.31,40.4c-7.65,0 -10.42,-5.82 -10.42,-9.74a3.5,3.5 0,1 1,7 0c0.06,1.8 1.22,2.74 3.42,2.74 3.09,0 3.42,-1.91 3.42,-2.74a3.5,3.5 0,1 1,7 0C70.75,34.58 68,40.4 60.33,40.4Z" />
|
||||
</vector>
|
|
@ -29,13 +29,17 @@
|
|||
android:textAlignment="textStart" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
<com.m2049r.xmrwallet.widget.PasswordEntryView
|
||||
android:id="@+id/etWalletPassword"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/header_top_first"
|
||||
app:errorEnabled="true">
|
||||
app:boxStrokeErrorColor="@color/monerujoPrimary"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@drawable/ic_smiley_gunther_filled"
|
||||
app:errorIconTint="@color/monerujoPrimary"
|
||||
app:errorTextColor="@color/monerujoPrimary">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
style="@style/MoneroEdit"
|
||||
|
@ -46,7 +50,7 @@
|
|||
android:inputType="textVisiblePassword"
|
||||
android:textAlignment="textStart" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</com.m2049r.xmrwallet.widget.PasswordEntryView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llFingerprintAuth"
|
||||
|
|
|
@ -10,12 +10,16 @@
|
|||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
<com.m2049r.xmrwallet.widget.PasswordEntryView
|
||||
android:id="@+id/etWalletPasswordA"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:errorEnabled="true">
|
||||
app:boxStrokeErrorColor="@color/monerujoPrimary"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@drawable/ic_smiley_gunther_filled"
|
||||
app:errorIconTint="@color/monerujoPrimary"
|
||||
app:errorTextColor="@color/monerujoPrimary">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
style="@style/MoneroEdit"
|
||||
|
@ -25,7 +29,7 @@
|
|||
android:imeOptions="actionNext"
|
||||
android:inputType="textVisiblePassword"
|
||||
android:textAlignment="textStart" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</com.m2049r.xmrwallet.widget.PasswordEntryView>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/etWalletPasswordB"
|
||||
|
|
Loading…
Reference in New Issue