Allow to create passwords without lowercase letters. (#478)
* Clipboard now is cleared after manual copy * Spanish translation added * Spanish translations for commit messages updated * Now can generate passwords free of lowercase (for example only numbers, uppercase or symbols) Also it makes sure that uppercase is included when the generated char is ambiguous and discarded
This commit is contained in:
parent
723a40a216
commit
a819343c74
7 changed files with 89 additions and 16 deletions
|
@ -13,9 +13,13 @@ import android.widget.Button;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
import com.zeapo.pwdstore.pwgen.PasswordGenerator;
|
import com.zeapo.pwdstore.pwgen.PasswordGenerator;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -53,6 +57,9 @@ public class PasswordGeneratorDialogFragment extends DialogFragment {
|
||||||
checkBox = view.findViewById(R.id.uppercase);
|
checkBox = view.findViewById(R.id.uppercase);
|
||||||
checkBox.setChecked(!prefs.getBoolean("A", false));
|
checkBox.setChecked(!prefs.getBoolean("A", false));
|
||||||
|
|
||||||
|
checkBox = view.findViewById(R.id.lowercase);
|
||||||
|
checkBox.setChecked(!prefs.getBoolean("L", false));
|
||||||
|
|
||||||
checkBox = view.findViewById(R.id.ambiguous);
|
checkBox = view.findViewById(R.id.ambiguous);
|
||||||
checkBox.setChecked(!prefs.getBoolean("B", false));
|
checkBox.setChecked(!prefs.getBoolean("B", false));
|
||||||
|
|
||||||
|
@ -79,12 +86,22 @@ public class PasswordGeneratorDialogFragment extends DialogFragment {
|
||||||
final AlertDialog ad = builder.setTitle(this.getResources().getString(R.string.pwgen_title)).create();
|
final AlertDialog ad = builder.setTitle(this.getResources().getString(R.string.pwgen_title)).create();
|
||||||
ad.setOnShowListener(dialog -> {
|
ad.setOnShowListener(dialog -> {
|
||||||
setPreferences();
|
setPreferences();
|
||||||
|
try {
|
||||||
passwordText.setText(PasswordGenerator.INSTANCE.generate(getActivity().getApplicationContext()).get(0));
|
passwordText.setText(PasswordGenerator.INSTANCE.generate(getActivity().getApplicationContext()).get(0));
|
||||||
|
} catch (PasswordGenerator.PasswordGeneratorExeption e) {
|
||||||
|
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
passwordText.setText("");
|
||||||
|
}
|
||||||
|
|
||||||
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
|
Button b = ad.getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||||
b.setOnClickListener(v -> {
|
b.setOnClickListener(v -> {
|
||||||
setPreferences();
|
setPreferences();
|
||||||
|
try {
|
||||||
passwordText.setText(PasswordGenerator.INSTANCE.generate(callingActivity.getApplicationContext()).get(0));
|
passwordText.setText(PasswordGenerator.INSTANCE.generate(callingActivity.getApplicationContext()).get(0));
|
||||||
|
} catch (PasswordGenerator.PasswordGeneratorExeption e) {
|
||||||
|
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
passwordText.setText("");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return ad;
|
return ad;
|
||||||
|
@ -107,6 +124,10 @@ public class PasswordGeneratorDialogFragment extends DialogFragment {
|
||||||
if (!((CheckBox) getDialog().findViewById(R.id.pronounceable)).isChecked()) {
|
if (!((CheckBox) getDialog().findViewById(R.id.pronounceable)).isChecked()) {
|
||||||
preferences.add("s");
|
preferences.add("s");
|
||||||
}
|
}
|
||||||
|
if (!((CheckBox) getDialog().findViewById(R.id.lowercase)).isChecked()) {
|
||||||
|
preferences.add("L");
|
||||||
|
}
|
||||||
|
|
||||||
EditText editText = getDialog().findViewById(R.id.lengthNumber);
|
EditText editText = getDialog().findViewById(R.id.lengthNumber);
|
||||||
try {
|
try {
|
||||||
int length = Integer.valueOf(editText.getText().toString());
|
int length = Integer.valueOf(editText.getText().toString());
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
package com.zeapo.pwdstore.pwgen
|
package com.zeapo.pwdstore.pwgen
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.zeapo.pwdstore.R
|
||||||
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
object PasswordGenerator {
|
object PasswordGenerator {
|
||||||
internal const val DIGITS = 0x0001
|
internal const val DIGITS = 0x0001
|
||||||
internal const val UPPERS = 0x0002
|
internal const val UPPERS = 0x0002
|
||||||
internal const val SYMBOLS = 0x0004
|
internal const val SYMBOLS = 0x0004
|
||||||
internal const val AMBIGUOUS = 0x0008
|
internal const val AMBIGUOUS = 0x0008
|
||||||
internal const val NO_VOWELS = 0x0010
|
internal const val NO_VOWELS = 0x0010
|
||||||
|
internal const val LOWERS = 0x0020
|
||||||
|
|
||||||
internal const val DIGITS_STR = "0123456789"
|
internal const val DIGITS_STR = "0123456789"
|
||||||
internal const val UPPERS_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
internal const val UPPERS_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
@ -19,7 +23,7 @@ object PasswordGenerator {
|
||||||
internal const val VOWELS_STR = "01aeiouyAEIOUY"
|
internal const val VOWELS_STR = "01aeiouyAEIOUY"
|
||||||
|
|
||||||
// No a, c, n, h, H, C, 1, N
|
// No a, c, n, h, H, C, 1, N
|
||||||
private const val pwOptions = "0ABsvy"
|
private const val pwOptions = "0ABsvyL"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets password generation preferences.
|
* Sets password generation preferences.
|
||||||
|
@ -35,6 +39,7 @@ object PasswordGenerator {
|
||||||
* <tr><td>s</td><td>generate completely random passwords</td></tr>
|
* <tr><td>s</td><td>generate completely random passwords</td></tr>
|
||||||
* <tr><td>v</td><td>don't include vowels</td></tr>
|
* <tr><td>v</td><td>don't include vowels</td></tr>
|
||||||
* <tr><td>y</td><td>include at least one symbol</td></tr>
|
* <tr><td>y</td><td>include at least one symbol</td></tr>
|
||||||
|
* <tr><td>L</td><td>don't include lowercase letters</td></tr>
|
||||||
</table> *
|
</table> *
|
||||||
* @param numArgv numerical options for password generation: length of
|
* @param numArgv numerical options for password generation: length of
|
||||||
* generated passwords followed by number of passwords to
|
* generated passwords followed by number of passwords to
|
||||||
|
@ -76,17 +81,19 @@ object PasswordGenerator {
|
||||||
* preferences file 'PasswordGenerator'
|
* preferences file 'PasswordGenerator'
|
||||||
* @return list of generated passwords
|
* @return list of generated passwords
|
||||||
*/
|
*/
|
||||||
|
@Throws(PasswordGenerator.PasswordGeneratorExeption::class)
|
||||||
fun generate(ctx: Context): ArrayList<String> {
|
fun generate(ctx: Context): ArrayList<String> {
|
||||||
val prefs = ctx.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
val prefs = ctx.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
var phonemes = true
|
var phonemes = true
|
||||||
var pwgenFlags = DIGITS or UPPERS
|
var pwgenFlags = DIGITS or UPPERS or LOWERS
|
||||||
|
|
||||||
for (option in pwOptions.toCharArray()) {
|
for (option in pwOptions.toCharArray()) {
|
||||||
if (prefs.getBoolean(option.toString(), false)) {
|
if (prefs.getBoolean(option.toString(), false)) {
|
||||||
when (option) {
|
when (option) {
|
||||||
'0' -> pwgenFlags = pwgenFlags and DIGITS.inv()
|
'0' -> pwgenFlags = pwgenFlags and DIGITS.inv()
|
||||||
'A' -> pwgenFlags = pwgenFlags and UPPERS.inv()
|
'A' -> pwgenFlags = pwgenFlags and UPPERS.inv()
|
||||||
|
'L' -> pwgenFlags = pwgenFlags and LOWERS.inv()
|
||||||
'B' -> pwgenFlags = pwgenFlags or AMBIGUOUS
|
'B' -> pwgenFlags = pwgenFlags or AMBIGUOUS
|
||||||
's' -> phonemes = false
|
's' -> phonemes = false
|
||||||
'y' -> pwgenFlags = pwgenFlags or SYMBOLS
|
'y' -> pwgenFlags = pwgenFlags or SYMBOLS
|
||||||
|
@ -99,15 +106,27 @@ object PasswordGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
val length = prefs.getInt("length", 8)
|
val length = prefs.getInt("length", 8)
|
||||||
if (length < 5) {
|
var numCategories = 0
|
||||||
|
var categories = pwgenFlags and AMBIGUOUS.inv()
|
||||||
|
|
||||||
|
while (categories != 0) {
|
||||||
|
if (categories and 1 == 1)
|
||||||
|
numCategories++
|
||||||
|
categories = categories shr 1
|
||||||
|
}
|
||||||
|
if (numCategories == 0) {
|
||||||
|
throw PasswordGeneratorExeption(ctx.resources.getString(R.string.pwgen_no_chars_error))
|
||||||
|
}
|
||||||
|
if (length < numCategories) {
|
||||||
|
throw PasswordGeneratorExeption(ctx.resources.getString(R.string.pwgen_length_too_short_error))
|
||||||
|
}
|
||||||
|
if ((pwgenFlags and UPPERS) == 0 && (pwgenFlags and LOWERS) == 0) { // Only digits and/or symbols
|
||||||
|
phonemes = false
|
||||||
|
pwgenFlags = pwgenFlags and AMBIGUOUS.inv()
|
||||||
|
} else if (length < 5) {
|
||||||
phonemes = false
|
phonemes = false
|
||||||
}
|
}
|
||||||
if (length <= 2) {
|
|
||||||
pwgenFlags = pwgenFlags and UPPERS.inv()
|
|
||||||
}
|
|
||||||
if (length <= 1) {
|
|
||||||
pwgenFlags = pwgenFlags and DIGITS.inv()
|
|
||||||
}
|
|
||||||
|
|
||||||
val passwords = ArrayList<String>()
|
val passwords = ArrayList<String>()
|
||||||
val num = prefs.getInt("num", 1)
|
val num = prefs.getInt("num", 1)
|
||||||
|
@ -120,5 +139,7 @@ object PasswordGenerator {
|
||||||
}
|
}
|
||||||
return passwords
|
return passwords
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PasswordGeneratorExeption(string: String) : Exception(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ internal object Phonemes {
|
||||||
* <tr><td>1</td><td>include at least one uppercase letter</td></tr>
|
* <tr><td>1</td><td>include at least one uppercase letter</td></tr>
|
||||||
* <tr><td>2</td><td>include at least one symbol</td></tr>
|
* <tr><td>2</td><td>include at least one symbol</td></tr>
|
||||||
* <tr><td>3</td><td>don't include ambiguous characters</td></tr>
|
* <tr><td>3</td><td>don't include ambiguous characters</td></tr>
|
||||||
|
* <tr><td>5</td><td>include at least one lowercase letter</td></tr>
|
||||||
</table> *
|
</table> *
|
||||||
* @return the generated password
|
* @return the generated password
|
||||||
*/
|
*/
|
||||||
|
@ -119,7 +120,8 @@ internal object Phonemes {
|
||||||
|
|
||||||
// Handle UPPERS
|
// Handle UPPERS
|
||||||
if (pwFlags and PasswordGenerator.UPPERS > 0) {
|
if (pwFlags and PasswordGenerator.UPPERS > 0) {
|
||||||
if ((first || flags and CONSONANT > 0) && RandomNumberGenerator.number(10) < 2) {
|
if ((pwFlags and PasswordGenerator.LOWERS == 0) ||
|
||||||
|
(first || flags and CONSONANT > 0) && RandomNumberGenerator.number(10) < 2) {
|
||||||
val index = password.length - length
|
val index = password.length - length
|
||||||
password = password.substring(0, index) + str.toUpperCase()
|
password = password.substring(0, index) + str.toUpperCase()
|
||||||
featureFlags = featureFlags and PasswordGenerator.UPPERS.inv()
|
featureFlags = featureFlags and PasswordGenerator.UPPERS.inv()
|
||||||
|
@ -131,6 +133,17 @@ internal object Phonemes {
|
||||||
for (ambiguous in PasswordGenerator.AMBIGUOUS_STR.toCharArray()) {
|
for (ambiguous in PasswordGenerator.AMBIGUOUS_STR.toCharArray()) {
|
||||||
if (password.contains(ambiguous.toString())) {
|
if (password.contains(ambiguous.toString())) {
|
||||||
password = password.substring(0, curSize)
|
password = password.substring(0, curSize)
|
||||||
|
|
||||||
|
// Still have upper letters
|
||||||
|
if ((pwFlags and PasswordGenerator.UPPERS) > 0) {
|
||||||
|
featureFlags = featureFlags or PasswordGenerator.UPPERS
|
||||||
|
for (upper in PasswordGenerator.UPPERS_STR.toCharArray()) {
|
||||||
|
if (password.contains(upper.toString())) {
|
||||||
|
featureFlags = featureFlags and PasswordGenerator.UPPERS.inv()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ internal object RandomPasswordGenerator {
|
||||||
* <tr><td>2</td><td>include at least one symbol</td></tr>
|
* <tr><td>2</td><td>include at least one symbol</td></tr>
|
||||||
* <tr><td>3</td><td>don't include ambiguous characters</td></tr>
|
* <tr><td>3</td><td>don't include ambiguous characters</td></tr>
|
||||||
* <tr><td>4</td><td>don't include vowels</td></tr>
|
* <tr><td>4</td><td>don't include vowels</td></tr>
|
||||||
|
* <tr><td>5</td><td>include at least one lowercase</td></tr>
|
||||||
</table> *
|
</table> *
|
||||||
* @return the generated password
|
* @return the generated password
|
||||||
*/
|
*/
|
||||||
|
@ -33,7 +34,9 @@ internal object RandomPasswordGenerator {
|
||||||
if (pwFlags and PasswordGenerator.UPPERS > 0) {
|
if (pwFlags and PasswordGenerator.UPPERS > 0) {
|
||||||
bank += PasswordGenerator.UPPERS_STR
|
bank += PasswordGenerator.UPPERS_STR
|
||||||
}
|
}
|
||||||
|
if (pwFlags and PasswordGenerator.LOWERS > 0) {
|
||||||
bank += PasswordGenerator.LOWERS_STR
|
bank += PasswordGenerator.LOWERS_STR
|
||||||
|
}
|
||||||
if (pwFlags and PasswordGenerator.SYMBOLS > 0) {
|
if (pwFlags and PasswordGenerator.SYMBOLS > 0) {
|
||||||
bank += PasswordGenerator.SYMBOLS_STR
|
bank += PasswordGenerator.SYMBOLS_STR
|
||||||
}
|
}
|
||||||
|
@ -65,8 +68,11 @@ internal object RandomPasswordGenerator {
|
||||||
if (PasswordGenerator.SYMBOLS_STR.contains(`val`)) {
|
if (PasswordGenerator.SYMBOLS_STR.contains(`val`)) {
|
||||||
featureFlags = featureFlags and PasswordGenerator.SYMBOLS.inv()
|
featureFlags = featureFlags and PasswordGenerator.SYMBOLS.inv()
|
||||||
}
|
}
|
||||||
|
if (PasswordGenerator.LOWERS_STR.contains(`val`)) {
|
||||||
|
featureFlags = featureFlags and PasswordGenerator.LOWERS.inv()
|
||||||
}
|
}
|
||||||
} while (featureFlags and (PasswordGenerator.UPPERS or PasswordGenerator.DIGITS or PasswordGenerator.SYMBOLS) > 0)
|
}
|
||||||
|
} while (featureFlags and (PasswordGenerator.UPPERS or PasswordGenerator.DIGITS or PasswordGenerator.SYMBOLS or PasswordGenerator.LOWERS) > 0)
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,10 +62,10 @@
|
||||||
android:text="@string/pwgen_uppercase" />
|
android:text="@string/pwgen_uppercase" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/ambiguous"
|
android:id="@+id/lowercase"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/pwgen_ambiguous" />
|
android:text="@string/pwgen_lowercase" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -97,6 +97,12 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/pwgen_pronounceable" />
|
android:text="@string/pwgen_pronounceable" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/ambiguous"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/pwgen_ambiguous" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -166,8 +166,11 @@
|
||||||
<string name="pwgen_numerals">Números</string>
|
<string name="pwgen_numerals">Números</string>
|
||||||
<string name="pwgen_symbols">Símbolos</string>
|
<string name="pwgen_symbols">Símbolos</string>
|
||||||
<string name="pwgen_uppercase">Mayúsculas</string>
|
<string name="pwgen_uppercase">Mayúsculas</string>
|
||||||
|
<string name="pwgen_lowercase">Minúsculas</string>
|
||||||
<string name="pwgen_ambiguous">Caracteres ambiguos</string>
|
<string name="pwgen_ambiguous">Caracteres ambiguos</string>
|
||||||
<string name="pwgen_pronounceable">Pronunciable</string>
|
<string name="pwgen_pronounceable">Pronunciable</string>
|
||||||
|
<string name="pwgen_no_chars_error">No se han incluído caracteres</string>
|
||||||
|
<string name="pwgen_length_too_short_error">Longitud demasiado corta para el criterio seleccionado</string>
|
||||||
|
|
||||||
<!-- ssh keygen fragment -->
|
<!-- ssh keygen fragment -->
|
||||||
<string name="ssh_keygen_length">Longitud</string>
|
<string name="ssh_keygen_length">Longitud</string>
|
||||||
|
|
|
@ -180,8 +180,11 @@
|
||||||
<string name="pwgen_numerals">Numerals</string>
|
<string name="pwgen_numerals">Numerals</string>
|
||||||
<string name="pwgen_symbols">Symbols</string>
|
<string name="pwgen_symbols">Symbols</string>
|
||||||
<string name="pwgen_uppercase">Uppercase</string>
|
<string name="pwgen_uppercase">Uppercase</string>
|
||||||
|
<string name="pwgen_lowercase">Lowercase</string>
|
||||||
<string name="pwgen_ambiguous">Ambiguous</string>
|
<string name="pwgen_ambiguous">Ambiguous</string>
|
||||||
<string name="pwgen_pronounceable">Pronounceable</string>
|
<string name="pwgen_pronounceable">Pronounceable</string>
|
||||||
|
<string name="pwgen_no_chars_error">No characters included</string>
|
||||||
|
<string name="pwgen_length_too_short_error">Length too short for selected criteria</string>
|
||||||
|
|
||||||
<!-- ssh keygen fragment -->
|
<!-- ssh keygen fragment -->
|
||||||
<string name="ssh_keygen_length">Length</string>
|
<string name="ssh_keygen_length">Length</string>
|
||||||
|
|
Loading…
Reference in a new issue