app: remove XkPasswd generator
This commit is contained in:
parent
0f837b0033
commit
a700e2a766
17 changed files with 15 additions and 9622 deletions
|
@ -44,7 +44,6 @@ import dev.msfjarvis.aps.data.repo.PasswordRepository
|
|||
import dev.msfjarvis.aps.databinding.PasswordCreationActivityBinding
|
||||
import dev.msfjarvis.aps.ui.dialogs.OtpImportDialogFragment
|
||||
import dev.msfjarvis.aps.ui.dialogs.PasswordGeneratorDialogFragment
|
||||
import dev.msfjarvis.aps.ui.dialogs.XkPasswordGeneratorDialogFragment
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillPreferences
|
||||
import dev.msfjarvis.aps.util.autofill.DirectoryStructure
|
||||
import dev.msfjarvis.aps.util.crypto.GpgIdentifier
|
||||
|
@ -361,8 +360,6 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) ?: KEY_PWGEN_TYPE_CLASSIC) {
|
||||
KEY_PWGEN_TYPE_CLASSIC ->
|
||||
PasswordGeneratorDialogFragment().show(supportFragmentManager, "generator")
|
||||
KEY_PWGEN_TYPE_XKPASSWD ->
|
||||
XkPasswordGeneratorDialogFragment().show(supportFragmentManager, "xkpwgenerator")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -594,7 +591,6 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||
companion object {
|
||||
|
||||
private const val KEY_PWGEN_TYPE_CLASSIC = "classic"
|
||||
private const val KEY_PWGEN_TYPE_XKPASSWD = "xkpasswd"
|
||||
const val PASSWORD_RESULT_REQUEST_KEY = "PASSWORD_GENERATOR"
|
||||
const val OTP_RESULT_REQUEST_KEY = "OTP_IMPORT"
|
||||
const val RESULT = "RESULT"
|
||||
|
|
|
@ -41,7 +41,6 @@ import dev.msfjarvis.aps.databinding.PasswordCreationActivityBinding
|
|||
import dev.msfjarvis.aps.injection.crypto.CryptoSet
|
||||
import dev.msfjarvis.aps.ui.dialogs.OtpImportDialogFragment
|
||||
import dev.msfjarvis.aps.ui.dialogs.PasswordGeneratorDialogFragment
|
||||
import dev.msfjarvis.aps.ui.dialogs.XkPasswordGeneratorDialogFragment
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillPreferences
|
||||
import dev.msfjarvis.aps.util.autofill.DirectoryStructure
|
||||
import dev.msfjarvis.aps.util.extensions.asLog
|
||||
|
@ -291,8 +290,6 @@ class PasswordCreationActivityV2 : BasePgpActivity() {
|
|||
when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) ?: KEY_PWGEN_TYPE_CLASSIC) {
|
||||
KEY_PWGEN_TYPE_CLASSIC ->
|
||||
PasswordGeneratorDialogFragment().show(supportFragmentManager, "generator")
|
||||
KEY_PWGEN_TYPE_XKPASSWD ->
|
||||
XkPasswordGeneratorDialogFragment().show(supportFragmentManager, "xkpwgenerator")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,7 +466,6 @@ class PasswordCreationActivityV2 : BasePgpActivity() {
|
|||
companion object {
|
||||
|
||||
private const val KEY_PWGEN_TYPE_CLASSIC = "classic"
|
||||
private const val KEY_PWGEN_TYPE_XKPASSWD = "xkpasswd"
|
||||
const val PASSWORD_RESULT_REQUEST_KEY = "PASSWORD_GENERATOR"
|
||||
const val OTP_RESULT_REQUEST_KEY = "OTP_IMPORT"
|
||||
const val RESULT = "RESULT"
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
package dev.msfjarvis.aps.ui.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.michaelbull.result.fold
|
||||
import com.github.michaelbull.result.getOr
|
||||
import com.github.michaelbull.result.runCatching
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.databinding.FragmentXkpwgenBinding
|
||||
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity
|
||||
import dev.msfjarvis.aps.util.extensions.asLog
|
||||
import dev.msfjarvis.aps.util.extensions.getString
|
||||
import dev.msfjarvis.aps.util.pwgenxkpwd.CapsType
|
||||
import dev.msfjarvis.aps.util.pwgenxkpwd.PasswordBuilder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
import reactivecircus.flowbinding.android.widget.afterTextChanges
|
||||
import reactivecircus.flowbinding.android.widget.selectionEvents
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class XkPasswordGeneratorDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||
val callingActivity = requireActivity()
|
||||
val inflater = callingActivity.layoutInflater
|
||||
val binding = FragmentXkpwgenBinding.inflate(inflater)
|
||||
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
|
||||
val prefs = callingActivity.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
||||
|
||||
builder.setView(binding.root)
|
||||
|
||||
val previousStoredCapStyle: String =
|
||||
runCatching { prefs.getString(PREF_KEY_CAPITALS_STYLE)!! }.getOr(DEFAULT_CAPS_STYLE)
|
||||
|
||||
val lastCapitalsStyleIndex: Int =
|
||||
runCatching { CapsType.valueOf(previousStoredCapStyle).ordinal }.getOr(DEFAULT_CAPS_INDEX)
|
||||
binding.xkCapType.setSelection(lastCapitalsStyleIndex)
|
||||
binding.xkNumWords.setText(prefs.getString(PREF_KEY_NUM_WORDS, DEFAULT_NUMBER_OF_WORDS))
|
||||
|
||||
binding.xkSeparator.setText(prefs.getString(PREF_KEY_SEPARATOR, DEFAULT_WORD_SEPARATOR))
|
||||
binding.xkNumberSymbolMask.setText(
|
||||
prefs.getString(PREF_KEY_EXTRA_SYMBOLS_MASK, DEFAULT_EXTRA_SYMBOLS_MASK)
|
||||
)
|
||||
|
||||
binding.xkPasswordText.typeface = monoTypeface
|
||||
|
||||
builder.setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ ->
|
||||
setPreferences(binding, prefs)
|
||||
setFragmentResult(
|
||||
PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY,
|
||||
bundleOf(PasswordCreationActivity.RESULT to "${binding.xkPasswordText.text}")
|
||||
)
|
||||
}
|
||||
|
||||
// flip neutral and negative buttons
|
||||
builder.setNeutralButton(resources.getString(R.string.dialog_cancel)) { _, _ -> }
|
||||
builder.setNegativeButton(resources.getString(R.string.pwgen_generate), null)
|
||||
|
||||
val dialog = builder.setTitle(this.resources.getString(R.string.xkpwgen_title)).create()
|
||||
|
||||
// make parameter changes reactive and automatically update passwords
|
||||
merge(
|
||||
binding.xkSeparator.afterTextChanges().skipInitialValue(),
|
||||
binding.xkCapType.selectionEvents().skipInitialValue(),
|
||||
binding.xkNumWords.afterTextChanges().skipInitialValue(),
|
||||
binding.xkNumberSymbolMask.afterTextChanges().skipInitialValue(),
|
||||
)
|
||||
.onEach { updatePassword(binding, prefs) }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
dialog.setOnShowListener {
|
||||
updatePassword(binding, prefs)
|
||||
|
||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
|
||||
updatePassword(binding, prefs)
|
||||
}
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun updatePassword(binding: FragmentXkpwgenBinding, prefs: SharedPreferences) {
|
||||
setPreferences(binding, prefs)
|
||||
makeAndSetPassword(binding)
|
||||
}
|
||||
|
||||
private fun makeAndSetPassword(binding: FragmentXkpwgenBinding) {
|
||||
PasswordBuilder(requireContext())
|
||||
.setNumberOfWords(binding.xkNumWords.text.toString().ifBlank { "0" }.toInt())
|
||||
.setMinimumWordLength(DEFAULT_MIN_WORD_LENGTH)
|
||||
.setMaximumWordLength(DEFAULT_MAX_WORD_LENGTH)
|
||||
.setSeparator(binding.xkSeparator.text.toString())
|
||||
.appendNumbers(
|
||||
binding.xkNumberSymbolMask.text!!.count { c -> c == EXTRA_CHAR_PLACEHOLDER_DIGIT }
|
||||
)
|
||||
.appendSymbols(
|
||||
binding.xkNumberSymbolMask.text!!.count { c -> c == EXTRA_CHAR_PLACEHOLDER_SYMBOL }
|
||||
)
|
||||
.setCapitalization(CapsType.valueOf(binding.xkCapType.selectedItem.toString()))
|
||||
.create()
|
||||
.fold(
|
||||
success = { binding.xkPasswordText.text = it },
|
||||
failure = { e ->
|
||||
Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show()
|
||||
logcat("xkpw", ERROR) { e.asLog("failure generating xkpasswd") }
|
||||
binding.xkPasswordText.text = FALLBACK_ERROR_PASS
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun setPreferences(binding: FragmentXkpwgenBinding, prefs: SharedPreferences) {
|
||||
prefs.edit {
|
||||
putString(PREF_KEY_CAPITALS_STYLE, binding.xkCapType.selectedItem.toString())
|
||||
putString(PREF_KEY_NUM_WORDS, binding.xkNumWords.text.toString())
|
||||
putString(PREF_KEY_SEPARATOR, binding.xkSeparator.text.toString())
|
||||
putString(PREF_KEY_EXTRA_SYMBOLS_MASK, binding.xkNumberSymbolMask.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val PREF_KEY_CAPITALS_STYLE = "pref_key_capitals_style"
|
||||
const val PREF_KEY_NUM_WORDS = "pref_key_num_words"
|
||||
const val PREF_KEY_SEPARATOR = "pref_key_separator"
|
||||
const val PREF_KEY_EXTRA_SYMBOLS_MASK = "pref_key_xkpwgen_extra_symbols_mask"
|
||||
val DEFAULT_CAPS_STYLE = CapsType.Sentence.name
|
||||
val DEFAULT_CAPS_INDEX = CapsType.Sentence.ordinal
|
||||
const val DEFAULT_NUMBER_OF_WORDS = "3"
|
||||
const val DEFAULT_WORD_SEPARATOR = "."
|
||||
const val DEFAULT_EXTRA_SYMBOLS_MASK = "ds"
|
||||
const val DEFAULT_MIN_WORD_LENGTH = 3
|
||||
const val DEFAULT_MAX_WORD_LENGTH = 9
|
||||
const val FALLBACK_ERROR_PASS = "42"
|
||||
const val EXTRA_CHAR_PLACEHOLDER_DIGIT = 'd'
|
||||
const val EXTRA_CHAR_PLACEHOLDER_SYMBOL = 's'
|
||||
}
|
||||
}
|
|
@ -6,78 +6,20 @@
|
|||
package dev.msfjarvis.aps.ui.settings
|
||||
|
||||
import android.text.InputType
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import de.Maxr1998.modernpreferences.Preference
|
||||
import de.Maxr1998.modernpreferences.PreferenceScreen
|
||||
import de.Maxr1998.modernpreferences.helpers.checkBox
|
||||
import de.Maxr1998.modernpreferences.helpers.editText
|
||||
import de.Maxr1998.modernpreferences.helpers.onCheckedChange
|
||||
import de.Maxr1998.modernpreferences.helpers.onClick
|
||||
import de.Maxr1998.modernpreferences.helpers.onSelectionChange
|
||||
import de.Maxr1998.modernpreferences.helpers.singleChoice
|
||||
import de.Maxr1998.modernpreferences.preferences.CheckBoxPreference
|
||||
import de.Maxr1998.modernpreferences.preferences.choice.SelectionItem
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.util.extensions.getString
|
||||
import dev.msfjarvis.aps.util.extensions.sharedPrefs
|
||||
import dev.msfjarvis.aps.util.extensions.unsafeLazy
|
||||
import dev.msfjarvis.aps.util.pwgenxkpwd.XkpwdDictionary
|
||||
import dev.msfjarvis.aps.util.settings.PreferenceKeys
|
||||
import java.io.File
|
||||
|
||||
class PasswordSettings(private val activity: FragmentActivity) : SettingsProvider {
|
||||
|
||||
private val sharedPrefs by unsafeLazy { activity.sharedPrefs }
|
||||
private val storeCustomXkpwdDictionaryAction =
|
||||
activity.registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
|
||||
Toast.makeText(
|
||||
activity,
|
||||
activity.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
|
||||
sharedPrefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) }
|
||||
|
||||
val inputStream = activity.contentResolver.openInputStream(uri)
|
||||
val customDictFile =
|
||||
File(activity.filesDir.toString(), XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE).outputStream()
|
||||
inputStream?.copyTo(customDictFile, 1024)
|
||||
inputStream?.close()
|
||||
customDictFile.close()
|
||||
}
|
||||
|
||||
override fun provideSettings(builder: PreferenceScreen.Builder) {
|
||||
builder.apply {
|
||||
val customDictPref =
|
||||
CheckBoxPreference(PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT).apply {
|
||||
titleRes = R.string.pref_xkpwgen_custom_wordlist_enabled_title
|
||||
summaryRes = R.string.pref_xkpwgen_custom_dict_summary_off
|
||||
summaryOnRes = R.string.pref_xkpwgen_custom_dict_summary_on
|
||||
visible = sharedPrefs.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) == "xkpasswd"
|
||||
onCheckedChange {
|
||||
requestRebind()
|
||||
true
|
||||
}
|
||||
}
|
||||
val customDictPathPref =
|
||||
Preference(PreferenceKeys.PREF_KEY_CUSTOM_DICT).apply {
|
||||
dependency = PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT
|
||||
titleRes = R.string.pref_xkpwgen_custom_dict_picker_title
|
||||
summary =
|
||||
sharedPrefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
|
||||
?: activity.resources.getString(R.string.pref_xkpwgen_custom_dict_picker_summary)
|
||||
visible = sharedPrefs.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) == "xkpasswd"
|
||||
onClick {
|
||||
storeCustomXkpwdDictionaryAction.launch(arrayOf("*/*"))
|
||||
true
|
||||
}
|
||||
}
|
||||
val values = activity.resources.getStringArray(R.array.pwgen_provider_values)
|
||||
val labels = activity.resources.getStringArray(R.array.pwgen_provider_labels)
|
||||
val items = values.zip(labels).map { SelectionItem(it.first, it.second, null) }
|
||||
|
@ -87,19 +29,8 @@ class PasswordSettings(private val activity: FragmentActivity) : SettingsProvide
|
|||
) {
|
||||
initialSelection = "classic"
|
||||
titleRes = R.string.pref_password_generator_type_title
|
||||
onSelectionChange { selection ->
|
||||
val xkpasswdEnabled = selection == "xkpasswd"
|
||||
customDictPathPref.visible = xkpasswdEnabled
|
||||
customDictPref.visible = xkpasswdEnabled
|
||||
customDictPref.requestRebind()
|
||||
customDictPathPref.requestRebind()
|
||||
true
|
||||
}
|
||||
onSelectionChange { true }
|
||||
}
|
||||
// We initialize them early and add them manually to be able to manually force a rebind
|
||||
// when the password generator type is changed.
|
||||
addPreferenceItem(customDictPref)
|
||||
addPreferenceItem(customDictPathPref)
|
||||
editText(PreferenceKeys.GENERAL_SHOW_TIME) {
|
||||
titleRes = R.string.pref_clipboard_timeout_title
|
||||
summaryProvider =
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
package dev.msfjarvis.aps.util.pwgenxkpwd
|
||||
|
||||
enum class CapsType {
|
||||
lowercase,
|
||||
UPPERCASE,
|
||||
TitleCase,
|
||||
Sentence,
|
||||
As_iS
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
package dev.msfjarvis.aps.util.pwgenxkpwd
|
||||
|
||||
import android.content.Context
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.runCatching
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator.PasswordGeneratorException
|
||||
import dev.msfjarvis.aps.util.pwgen.secureRandomCharacter
|
||||
import dev.msfjarvis.aps.util.pwgen.secureRandomElement
|
||||
import dev.msfjarvis.aps.util.pwgen.secureRandomNumber
|
||||
import java.util.Locale
|
||||
|
||||
class PasswordBuilder(ctx: Context) {
|
||||
|
||||
private var numSymbols = 0
|
||||
private var isAppendSymbolsSeparator = false
|
||||
private var context = ctx
|
||||
private var numWords = 3
|
||||
private var maxWordLength = 9
|
||||
private var minWordLength = 5
|
||||
private var separator = "."
|
||||
private var capsType = CapsType.Sentence
|
||||
private var prependDigits = 0
|
||||
private var numDigits = 0
|
||||
private var isPrependWithSeparator = false
|
||||
private var isAppendNumberSeparator = false
|
||||
|
||||
fun setNumberOfWords(amount: Int) = apply { numWords = amount }
|
||||
|
||||
fun setMinimumWordLength(min: Int) = apply { minWordLength = min }
|
||||
|
||||
fun setMaximumWordLength(max: Int) = apply { maxWordLength = max }
|
||||
|
||||
fun setSeparator(separator: String) = apply { this.separator = separator }
|
||||
|
||||
fun setCapitalization(capitalizationScheme: CapsType) = apply { capsType = capitalizationScheme }
|
||||
|
||||
@JvmOverloads
|
||||
fun prependNumbers(numDigits: Int, addSeparator: Boolean = true) = apply {
|
||||
prependDigits = numDigits
|
||||
isPrependWithSeparator = addSeparator
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun appendNumbers(numDigits: Int, addSeparator: Boolean = false) = apply {
|
||||
this.numDigits = numDigits
|
||||
isAppendNumberSeparator = addSeparator
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun appendSymbols(numSymbols: Int, addSeparator: Boolean = false) = apply {
|
||||
this.numSymbols = numSymbols
|
||||
isAppendSymbolsSeparator = addSeparator
|
||||
}
|
||||
|
||||
private fun generateRandomNumberSequence(totalNumbers: Int): String {
|
||||
val numbers = StringBuilder(totalNumbers)
|
||||
for (i in 0 until totalNumbers) {
|
||||
numbers.append(secureRandomNumber(10))
|
||||
}
|
||||
return numbers.toString()
|
||||
}
|
||||
|
||||
private fun generateRandomSymbolSequence(numSymbols: Int): String {
|
||||
val numbers = StringBuilder(numSymbols)
|
||||
for (i in 0 until numSymbols) {
|
||||
numbers.append(SYMBOLS.secureRandomCharacter())
|
||||
}
|
||||
return numbers.toString()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
fun create(): Result<String, Throwable> {
|
||||
val wordBank = mutableListOf<String>()
|
||||
val password = StringBuilder()
|
||||
|
||||
if (prependDigits != 0) {
|
||||
password.append(generateRandomNumberSequence(prependDigits))
|
||||
if (isPrependWithSeparator) {
|
||||
password.append(separator)
|
||||
}
|
||||
}
|
||||
return runCatching {
|
||||
val dictionary = XkpwdDictionary(context)
|
||||
val words = dictionary.words
|
||||
for (wordLength in minWordLength..maxWordLength) {
|
||||
wordBank.addAll(words[wordLength] ?: emptyList())
|
||||
}
|
||||
|
||||
if (wordBank.size == 0) {
|
||||
throw PasswordGeneratorException(
|
||||
context.getString(R.string.xkpwgen_builder_error, minWordLength, maxWordLength)
|
||||
)
|
||||
}
|
||||
|
||||
for (i in 0 until numWords) {
|
||||
val candidate = wordBank.secureRandomElement()
|
||||
val s =
|
||||
when (capsType) {
|
||||
CapsType.UPPERCASE -> candidate.uppercase(Locale.getDefault())
|
||||
CapsType.Sentence ->
|
||||
if (i == 0)
|
||||
candidate.replaceFirstChar {
|
||||
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
|
||||
}
|
||||
else candidate
|
||||
CapsType.TitleCase ->
|
||||
candidate.replaceFirstChar {
|
||||
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
|
||||
}
|
||||
CapsType.lowercase -> candidate.lowercase(Locale.getDefault())
|
||||
CapsType.As_iS -> candidate
|
||||
}
|
||||
password.append(s)
|
||||
if (i + 1 < numWords) {
|
||||
password.append(separator)
|
||||
}
|
||||
}
|
||||
if (numDigits != 0) {
|
||||
if (isAppendNumberSeparator) {
|
||||
password.append(separator)
|
||||
}
|
||||
password.append(generateRandomNumberSequence(numDigits))
|
||||
}
|
||||
if (numSymbols != 0) {
|
||||
if (isAppendSymbolsSeparator) {
|
||||
password.append(separator)
|
||||
}
|
||||
password.append(generateRandomSymbolSequence(numSymbols))
|
||||
}
|
||||
password.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val SYMBOLS = "!@\$%^&*-_+=:|~?/.;#"
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
package dev.msfjarvis.aps.util.pwgenxkpwd
|
||||
|
||||
import android.content.Context
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.util.extensions.getString
|
||||
import dev.msfjarvis.aps.util.extensions.sharedPrefs
|
||||
import dev.msfjarvis.aps.util.settings.PreferenceKeys
|
||||
import java.io.File
|
||||
|
||||
class XkpwdDictionary(context: Context) {
|
||||
|
||||
val words: Map<Int, List<String>>
|
||||
|
||||
init {
|
||||
val prefs = context.sharedPrefs
|
||||
val uri = prefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT) ?: ""
|
||||
val customDictFile = File(context.filesDir, XKPWD_CUSTOM_DICT_FILE)
|
||||
|
||||
val lines =
|
||||
if (prefs.getBoolean(PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT, false) &&
|
||||
uri.isNotEmpty() &&
|
||||
customDictFile.canRead()
|
||||
) {
|
||||
customDictFile.readLines()
|
||||
} else {
|
||||
context.resources.openRawResource(R.raw.xkpwdict).bufferedReader().readLines()
|
||||
}
|
||||
|
||||
words =
|
||||
lines.asSequence().map { it.trim() }.filter { it.isNotEmpty() && !it.contains(' ') }.groupBy {
|
||||
it.length
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val XKPWD_CUSTOM_DICT_FILE = "custom_dict.txt"
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
<!--
|
||||
~ Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
~ SPDX-License-Identifier: GPL-3.0-only
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/xkPasswordText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textIsSelectable="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="horisticia.tockmendprost" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/total_words"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:hint="@string/xkpwgen_length"
|
||||
android:labelFor="@id/xk_num_words"
|
||||
app:layout_constraintEnd_toStartOf="@id/separator"
|
||||
app:layout_constraintHorizontal_weight="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/xkPasswordText">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/xk_num_words"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number"
|
||||
android:maxLength="2" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:hint="@string/xkpwgen_separator"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_weight="0.5"
|
||||
app:layout_constraintStart_toEndOf="@id/total_words"
|
||||
app:layout_constraintTop_toTopOf="@id/total_words">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/xk_separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints=""
|
||||
android:inputType="text" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/xkCapType"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:entries="@array/capitalization_type_values"
|
||||
android:entryValues="@array/capitalization_type_values"
|
||||
android:spinnerMode="dropdown"
|
||||
app:layout_constraintEnd_toEndOf="@id/total_words"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/total_words" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/xk_numbers_symbols_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/xkpwgen_extrachars_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/separator"
|
||||
app:layout_constraintTop_toBottomOf="@id/separator"
|
||||
app:layout_constraintTop_toTopOf="@id/xkCapType">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/xk_number_symbol_mask"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints=""
|
||||
android:inputType="text"
|
||||
android:text="@string/xk_numbers_symbols_append_default" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
File diff suppressed because it is too large
Load diff
|
@ -138,19 +138,9 @@
|
|||
<string name="pwgen_no_chars_error">Keine Zeichen hinzugefügt</string>
|
||||
<string name="pwgen_length_too_short_error">Länge zu kurz für ausgewählte Kriterien</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Fehler beim Generieren eines Passworts, das die Einschränkungen erfüllt. Versuchen Sie, die Länge zu erhöhen.</string>
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Xkpasswd Generator</string>
|
||||
<string name="xkpwgen_length">Wörter insgesamt</string>
|
||||
<string name="xkpwgen_separator">Trennzeichen</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Eigene Wortliste: %1$s</string>
|
||||
<string name="xkpwgen_builder_error">Das ausgewählte Wörterbuch enthält nicht genügend Wörter der angegebenen Länge %1$d..%2$d</string>
|
||||
<!-- XKPWD prefs -->
|
||||
<string name="pwgen_separator">Trennzeichen</string>
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Passwortgenerator</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Eigene Wortliste</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Eigene Wordlist-Datei verwenden</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Integrierte Wortliste verwenden</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">Eigene Wortliste</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Tippen Sie, um eine benutzerdefinierte Wordlist-Datei mit einem Wort pro Zeile auszuwählen</string>
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Passwort</string>
|
||||
<string name="ssh_keygen_generate">Generieren</string>
|
||||
|
|
|
@ -142,19 +142,9 @@
|
|||
<string name="pwgen_no_chars_error">Aucun critère sélectionné</string>
|
||||
<string name="pwgen_length_too_short_error">Longueur trop faible pour les critères sélectionnés</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Impossible de générer un mot de passe satisfaisant les critères. Essayez d\'augmenter la longueur.</string>
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Générateur XKCD</string>
|
||||
<string name="xkpwgen_length">Nombre de mots</string>
|
||||
<string name="xkpwgen_separator">Séparateur</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Liste de mots personnalisée : %1$s</string>
|
||||
<string name="xkpwgen_builder_error">Le dictionnaire sélectionné ne contient pas assez de mots de la longueur %1$d..%2$d</string>
|
||||
<!-- XKPWD prefs -->
|
||||
<string name="pwgen_separator">Séparateur</string>
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Type de générateur de mot de passe</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Liste de mots personnalisée</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Utiliser un fichier comme liste de mots personnalisée</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Utilisation de la liste de mots intégrée</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">Fichier personnalisé de liste de mots</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Touchez pour choisir un fichier de liste de mots personnalisés contenant un mot par ligne</string>
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Mot de passe</string>
|
||||
<string name="ssh_keygen_generate">Générer</string>
|
||||
|
|
|
@ -142,19 +142,9 @@
|
|||
<string name="pwgen_no_chars_error">Sen caracteres incluídos</string>
|
||||
<string name="pwgen_length_too_short_error">Lonxitude demasiado curta para o criterio</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Fallou a creación do contrasinal satisfacendo os requerimentos. Intenta aumentar a lonxitude.</string>
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Xkpasswd Generator</string>
|
||||
<string name="xkpwgen_length">Total de palabras</string>
|
||||
<string name="xkpwgen_separator">Separador</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Lista persoal de palabras: %1$s</string>
|
||||
<string name="xkpwgen_builder_error">O dicionario non contén palabras suficientes da lonxitude dada %1$d .. %2$d</string>
|
||||
<!-- XKPWD prefs -->
|
||||
<string name="pwgen_separator">Separador</string>
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Tipo de creador de contrasinais</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Lista persoal de palabras</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Usar ficheiro con palabras personalizadas</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Usar lista de palabras incluída</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">Ficheiro persoal de palabras</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Toca para escoller un ficheiro persoal con palabras que conteña unha palabra por liña</string>
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Frase de paso</string>
|
||||
<string name="ssh_keygen_generate">Crear</string>
|
||||
|
|
|
@ -136,19 +136,9 @@
|
|||
<string name="pwgen_no_chars_error">Nessun carattere incluso</string>
|
||||
<string name="pwgen_length_too_short_error">Lunghezza troppo breve per i criteri selezionati</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Impossibile generare una password che soddisfi i vincoli. Prova ad aumentare la lunghezza.</string>
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Generatore di Xkpasswd</string>
|
||||
<string name="xkpwgen_length">Parole totali</string>
|
||||
<string name="xkpwgen_separator">Separatore</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Lista di parole personalizzata: %1$s</string>
|
||||
<string name="xkpwgen_builder_error">Il dizionario selezionato non contiene abbastanza parole della data lunghezza %1$d..%2$d</string>
|
||||
<!-- XKPWD prefs -->
|
||||
<string name="pwgen_separator">Separatore</string>
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Tipo di generatore di password</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Lista di parole personalizzata</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Usando file di elenco di parole personalizzati</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Usando liste di parole integrate</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">File di elenco di parole personalizzato</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Tocca per selezionare un file di lista di parole personalizzato contenente una parola per riga</string>
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Frase Segreta</string>
|
||||
<string name="ssh_keygen_generate">Genera</string>
|
||||
|
|
|
@ -142,19 +142,9 @@
|
|||
<string name="pwgen_no_chars_error">Nenhum caractere incluso</string>
|
||||
<string name="pwgen_length_too_short_error">Comprimento muito curto para os critérios selecionados</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Falha ao gerar uma senha de acordo com as restrições. Tente aumentar o comprimento.</string>
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Xkpasswd Generator</string>
|
||||
<string name="xkpwgen_length">Total de palavras</string>
|
||||
<string name="xkpwgen_separator">Separador</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Lista de palavras personalizada: %1$s</string>
|
||||
<string name="xkpwgen_builder_error">O dicionário selecionado não contém palavras suficientes de tamanho %1$d..%2$d</string>
|
||||
<!-- XKPWD prefs -->
|
||||
<string name="pwgen_separator">Separador</string>
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Tipo de gerador de senha</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Lista de palavras personalizadas</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Usando um arquivo de Lista de Palavras</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Usando o arquivo de palavras embutido</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">Lista de palavras personalizadas</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Toque para escolher um arquivo personalizado de lista de palavras contendo uma palavra por linha</string>
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Frase Secreta</string>
|
||||
<string name="ssh_keygen_generate">Gerar</string>
|
||||
|
|
|
@ -141,19 +141,9 @@
|
|||
<string name="pwgen_no_chars_error">Не включать символы</string>
|
||||
<string name="pwgen_length_too_short_error">Длина не соответствует выбранным критериям</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Не удалось создать пароль, удовлетворяющий ограничениям. Попробуйте увеличить длину.</string>
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Генератор Xkpasswd</string>
|
||||
<string name="xkpwgen_length">Всего слов</string>
|
||||
<string name="xkpwgen_separator">Разделитель</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Пользовательский список слов: %1$s</string>
|
||||
<string name="xkpwgen_builder_error">Выбранный словарь не содержит достаточного количества слова заданной длинны %1$d..%2$d</string>
|
||||
<!-- XKPWD prefs -->
|
||||
<string name="pwgen_separator">Разделитель</string>
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Тип генератора паролей</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Пользовательский список слов</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Использовать файл списка слов созданный пользователем</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Использовать встроенный список слов</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">Файл пользовательского списка слов</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Нажмите чтобы выбрать файл пользовательского списка слов содержащий одно слово на строку</string>
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Пароль</string>
|
||||
<string name="ssh_keygen_generate">Сгенерировать</string>
|
||||
|
|
|
@ -24,11 +24,9 @@
|
|||
</string-array>
|
||||
<string-array name="pwgen_provider_labels">
|
||||
<item>Classic</item>
|
||||
<item>XKPasswd</item>
|
||||
</string-array>
|
||||
<string-array name="pwgen_provider_values">
|
||||
<item>classic</item>
|
||||
<item>xkpasswd</item>
|
||||
</string-array>
|
||||
<string-array name="oreo_autofill_directory_structure_entries">
|
||||
<item>work/example.org(.gpg)</item>
|
||||
|
|
|
@ -160,21 +160,10 @@
|
|||
<string name="pwgen_no_chars_error">No characters included</string>
|
||||
<string name="pwgen_length_too_short_error">Length too short for selected criteria</string>
|
||||
<string name="pwgen_max_iterations_exceeded">Failed to generate a password satisfying the constraints. Try to increase the length.</string>
|
||||
<string name="pwgen_separator">Separator</string>
|
||||
|
||||
<!-- XKPWD password generator -->
|
||||
<string name="xkpwgen_title">Xkpasswd Generator</string>
|
||||
<string name="xkpwgen_length">Total words</string>
|
||||
<string name="xkpwgen_separator">Separator</string>
|
||||
<string name="xkpwgen_custom_dict_imported">Custom wordlist: %1$s</string>
|
||||
<string name="xkpwgen_builder_error">Selected dictionary does not contain enough words of given length %1$d..%2$d</string>
|
||||
|
||||
<!-- XKPWD prefs -->
|
||||
<!-- Password generator prefs -->
|
||||
<string name="pref_password_generator_type_title">Password generator type</string>
|
||||
<string name="pref_xkpwgen_custom_wordlist_enabled_title">Custom wordlist</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_on">Using custom wordlist file</string>
|
||||
<string name="pref_xkpwgen_custom_dict_summary_off">Using built-in wordlist</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_title">Custom worldlist file</string>
|
||||
<string name="pref_xkpwgen_custom_dict_picker_summary">Tap to pick a custom wordlist file containing one word per line</string>
|
||||
|
||||
<!-- ssh keygen fragment -->
|
||||
<string name="ssh_keygen_passphrase">Passphrase</string>
|
||||
|
|
Loading…
Reference in a new issue