aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/com/zeapo
diff options
context:
space:
mode:
authorglowinthedark <48893368+glowinthedark@users.noreply.github.com>2020-02-29 21:46:31 +0100
committerGitHub <noreply@github.com>2020-03-01 02:16:31 +0530
commit947e41105b3fdfb7ce9734fb25119c46695f7bba (patch)
tree74c63b130b262a06ea068e10ffc20c1dd69d4897 /app/src/main/java/com/zeapo
parentbea3cd5457e331f04a9aeb5c99267f14908f5eaa (diff)
Add xkpasswd-style password generator (#633)
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src/main/java/com/zeapo')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/UserPreference.kt90
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt20
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/CapsType.kt9
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/PasswordBuilder.kt166
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt58
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/PasswordGeneratorDialogFragment.kt9
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/XkPasswordGeneratorDialogFragment.kt160
7 files changed, 503 insertions, 9 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
index 50d5bcad..24ed62df 100644
--- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
@@ -15,6 +15,7 @@ import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.Settings
+import android.text.TextUtils
import android.view.MenuItem
import android.view.accessibility.AccessibilityManager
import android.widget.Toast
@@ -23,6 +24,7 @@ import androidx.biometric.BiometricManager
import androidx.core.content.getSystemService
import androidx.documentfile.provider.DocumentFile
import androidx.preference.CheckBoxPreference
+import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
@@ -32,6 +34,7 @@ import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.autofill.AutofillPreferenceActivity
import com.zeapo.pwdstore.crypto.PgpActivity
import com.zeapo.pwdstore.git.GitActivity
+import com.zeapo.pwdstore.pwgenxkpwd.XkpwdDictionary
import com.zeapo.pwdstore.sshkeygen.ShowSshKeyFragment
import com.zeapo.pwdstore.sshkeygen.SshKeyGenActivity
import com.zeapo.pwdstore.utils.PasswordRepository
@@ -312,6 +315,51 @@ class UserPreference : AppCompatActivity() {
}
}
}
+
+ val prefCustomXkpwdDictionary = findPreference<Preference>("pref_key_custom_dict")
+ prefCustomXkpwdDictionary?.onPreferenceClickListener = ClickListener {
+ callingActivity.storeCustomDictionaryPath()
+ true
+ }
+ val dictUri = sharedPreferences.getString("pref_key_custom_dict", "")
+
+ if (!TextUtils.isEmpty(dictUri)) {
+ setCustomDictSummary(prefCustomXkpwdDictionary, Uri.parse(dictUri))
+ }
+
+ val prefIsCustomDict = findPreference<CheckBoxPreference>("pref_key_is_custom_dict")
+ val prefCustomDictPicker = findPreference<Preference>("pref_key_custom_dict")
+ val prefPwgenType = findPreference<ListPreference>("pref_key_pwgen_type")
+ showHideDependentPrefs(prefPwgenType?.value, prefIsCustomDict, prefCustomDictPicker)
+
+ prefPwgenType?.onPreferenceChangeListener = ChangeListener() { _, newValue ->
+ showHideDependentPrefs(newValue, prefIsCustomDict, prefCustomDictPicker)
+ true
+ }
+
+ prefIsCustomDict?.onPreferenceChangeListener = ChangeListener() { _, newValue ->
+ if (!(newValue as Boolean)) {
+ val customDictFile = File(context.filesDir, XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE)
+ if (customDictFile.exists()) {
+ FileUtils.deleteQuietly(customDictFile)
+ }
+ prefCustomDictPicker?.setSummary(R.string.xkpwgen_pref_custom_dict_picker_summary)
+ }
+ true
+ }
+ }
+
+ private fun showHideDependentPrefs(newValue: Any?, prefIsCustomDict: CheckBoxPreference?, prefCustomDictPicker: Preference?) {
+ when (newValue as String) {
+ PgpActivity.KEY_PWGEN_TYPE_CLASSIC -> {
+ prefIsCustomDict?.isVisible = false
+ prefCustomDictPicker?.isVisible = false
+ }
+ PgpActivity.KEY_PWGEN_TYPE_XKPASSWD -> {
+ prefIsCustomDict?.isVisible = true
+ prefCustomDictPicker?.isVisible = true
+ }
+ }
}
override fun onResume() {
@@ -396,6 +444,17 @@ class UserPreference : AppCompatActivity() {
}
}
+ /**
+ * Pick custom xkpwd dictionary from sdcard
+ */
+ private fun storeCustomDictionaryPath() {
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "*/*"
+ }
+ startActivityForResult(intent, SET_CUSTOM_XKPWD_DICT)
+ }
+
@Throws(IOException::class)
private fun copySshKey(uri: Uri) {
// TODO: Check if valid SSH Key before import
@@ -516,6 +575,27 @@ class UserPreference : AppCompatActivity() {
}
}
}
+ SET_CUSTOM_XKPWD_DICT -> {
+ val uri: Uri = data.data ?: throw IOException("Unable to open file")
+
+ Toast.makeText(
+ this,
+ this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
+ Toast.LENGTH_SHORT
+ ).show()
+ val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+
+ prefs.edit().putString("pref_key_custom_dict", uri.toString()).apply()
+
+ val customDictPref = prefsFragment.findPreference<Preference>("pref_key_custom_dict")
+ setCustomDictSummary(customDictPref, uri)
+ // copy user selected file to internal storage
+ val inputStream = this.contentResolver.openInputStream(uri)
+ val customDictFile = File(this.filesDir.toString(), XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE)
+ FileUtils.copyInputStreamToFile(inputStream, customDictFile)
+
+ setResult(Activity.RESULT_OK)
+ }
}
}
super.onActivityResult(requestCode, resultCode, data)
@@ -599,6 +679,16 @@ class UserPreference : AppCompatActivity() {
private const val SELECT_GIT_DIRECTORY = 4
private const val EXPORT_PASSWORDS = 5
private const val EDIT_GIT_CONFIG = 6
+ private const val SET_CUSTOM_XKPWD_DICT = 7
private const val TAG = "UserPreference"
+
+ /**
+ * Set custom dictionary summary
+ */
+ @JvmStatic
+ private fun setCustomDictSummary(customDictPref: Preference?, uri: Uri) {
+ val fileName = uri.path?.substring(uri.path?.lastIndexOf(":")!! + 1)
+ customDictPref?.setSummary("Selected dictionary: " + fileName)
+ }
}
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
index 4e49dfe3..42878171 100644
--- a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
@@ -38,6 +38,7 @@ import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.UserPreference
import com.zeapo.pwdstore.ui.dialogs.PasswordGeneratorDialogFragment
+import com.zeapo.pwdstore.ui.dialogs.XkPasswordGeneratorDialogFragment
import com.zeapo.pwdstore.utils.Otp
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -142,8 +143,12 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
setContentView(R.layout.encrypt_layout)
generate_password?.setOnClickListener {
- PasswordGeneratorDialogFragment()
- .show(supportFragmentManager, "generator")
+ when (settings.getString("pref_key_pwgen_type", KEY_PWGEN_TYPE_CLASSIC)) {
+ KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment()
+ .show(supportFragmentManager, "generator")
+ KEY_PWGEN_TYPE_XKPASSWD -> XkPasswordGeneratorDialogFragment()
+ .show(supportFragmentManager, "xkpwgenerator")
+ }
}
title = getString(R.string.new_password_title)
@@ -505,8 +510,12 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
private fun editPassword() {
setContentView(R.layout.encrypt_layout)
generate_password?.setOnClickListener {
- PasswordGeneratorDialogFragment()
- .show(supportFragmentManager, "generator")
+ when (settings.getString("pref_key_pwgen_type", KEY_PWGEN_TYPE_CLASSIC)) {
+ KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment()
+ .show(supportFragmentManager, "generator")
+ KEY_PWGEN_TYPE_XKPASSWD -> XkPasswordGeneratorDialogFragment()
+ .show(supportFragmentManager, "xkpwgenerator")
+ }
}
title = getString(R.string.edit_password_title)
@@ -853,6 +862,9 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
const val TAG = "PgpActivity"
+ const val KEY_PWGEN_TYPE_CLASSIC = "classic"
+ const val KEY_PWGEN_TYPE_XKPASSWD = "xkpasswd"
+
private var delayTask: DelayShow? = null
/**
diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/CapsType.kt b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/CapsType.kt
new file mode 100644
index 00000000..a0311b8d
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/CapsType.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.pwgenxkpwd
+
+enum class CapsType {
+ lowercase, UPPERCASE, TitleCase, Sentencecase, As_iS
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/PasswordBuilder.kt b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/PasswordBuilder.kt
new file mode 100644
index 00000000..a526490f
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/PasswordBuilder.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.pwgenxkpwd
+
+import android.content.Context
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.pwgen.PasswordGenerator
+import com.zeapo.pwdstore.pwgen.PasswordGenerator.PasswordGeneratorExeption
+import java.io.IOException
+import java.security.SecureRandom
+import java.util.ArrayList
+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.Sentencecase
+ 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 secureRandom = SecureRandom()
+ val numbers = StringBuilder(totalNumbers)
+
+ for (i in 0 until totalNumbers) {
+ numbers.append(secureRandom.nextInt(10))
+ }
+ return numbers.toString()
+ }
+
+ private fun generateRandomSymbolSequence(numSymbols: Int): String {
+ val secureRandom = SecureRandom()
+ val numbers = StringBuilder(numSymbols)
+
+ for (i in 0 until numSymbols) {
+ numbers.append(SYMBOLS[secureRandom.nextInt(SYMBOLS.length)])
+ }
+ return numbers.toString()
+ }
+
+ @Throws(PasswordGenerator.PasswordGeneratorExeption::class)
+ fun create(): String {
+ val wordBank = ArrayList<String>()
+ val secureRandom = SecureRandom()
+ val password = StringBuilder()
+
+ if (prependDigits != 0) {
+ password.append(generateRandomNumberSequence(prependDigits))
+ if (isPrependWithSeparator) {
+ password.append(separator)
+ }
+ }
+ try {
+ val dictionary = XkpwdDictionary(context)
+ val words = dictionary.words
+ for (wordLength in words.keys) {
+ if (wordLength in minWordLength..maxWordLength) {
+ wordBank.addAll(words[wordLength]!!)
+ }
+ }
+
+ if (wordBank.size == 0) {
+ throw PasswordGeneratorExeption(context.getString(R.string.xkpwgen_builder_error, minWordLength, maxWordLength))
+ }
+
+ for (i in 0 until numWords) {
+ val randomIndex = secureRandom.nextInt(wordBank.size)
+ var s = wordBank[randomIndex]
+
+ if (capsType != CapsType.As_iS) {
+ s = s.toLowerCase(Locale.getDefault())
+ when (capsType) {
+ CapsType.UPPERCASE -> s = s.toUpperCase(Locale.getDefault())
+ CapsType.Sentencecase -> {
+ if (i == 0) {
+ s = capitalize(s)
+ }
+ }
+ CapsType.TitleCase -> {
+ s = capitalize(s)
+ }
+ }
+ }
+ password.append(s)
+ wordBank.removeAt(randomIndex)
+ if (i + 1 < numWords) {
+ password.append(separator)
+ }
+ }
+ } catch (e: IOException) {
+ throw PasswordGeneratorExeption("Failed generating password!")
+ }
+ if (numDigits != 0) {
+ if (isAppendNumberSeparator) {
+ password.append(separator)
+ }
+ password.append(generateRandomNumberSequence(numDigits))
+ }
+ if (numSymbols != 0) {
+ if (isAppendSymbolsSeparator) {
+ password.append(separator)
+ }
+ password.append(generateRandomSymbolSequence(numSymbols))
+ }
+ return password.toString()
+ }
+
+ private fun capitalize(s: String): String {
+ var result = s
+ val lower = result.toLowerCase(Locale.getDefault())
+ result = lower.substring(0, 1).toUpperCase(Locale.getDefault()) + result.substring(1)
+ return result
+ }
+
+ companion object {
+ private const val SYMBOLS = "!@\$%^&*-_+=:|~?/.;#"
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt
new file mode 100644
index 00000000..27f8fbd0
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.pwgenxkpwd
+
+import android.content.Context
+import android.text.TextUtils
+import androidx.preference.PreferenceManager
+import com.zeapo.pwdstore.R
+import java.io.File
+import java.util.ArrayList
+import java.util.HashMap
+
+class XkpwdDictionary(context: Context) {
+ val words: HashMap<Int, ArrayList<String>> = HashMap()
+
+ init {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+
+ var lines: List<String> = listOf()
+
+ if (prefs.getBoolean("pref_key_is_custom_dict", false)) {
+
+ val uri = prefs.getString("pref_key_custom_dict", "")
+
+ if (!TextUtils.isEmpty(uri)) {
+ val customDictFile = File(context.filesDir, XKPWD_CUSTOM_DICT_FILE)
+
+ if (customDictFile.exists() && customDictFile.canRead()) {
+ lines = customDictFile.inputStream().bufferedReader().readLines()
+ }
+ }
+ }
+
+ if (lines.isEmpty()) {
+ lines = context.getResources().openRawResource(R.raw.xkpwdict).bufferedReader().readLines()
+ }
+
+ for (word in lines) {
+ if (!word.trim { it <= ' ' }.contains(" ")) {
+ val length = word.trim { it <= ' ' }.length
+
+ if (length > 0) {
+ if (!words.containsKey(length)) {
+ words[length] = ArrayList()
+ }
+ val strings = words[length]!!
+ strings.add(word.trim { it <= ' ' })
+ }
+ }
+ }
+ }
+
+ companion object {
+ const val XKPWD_CUSTOM_DICT_FILE = "custom_dict.txt"
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/PasswordGeneratorDialogFragment.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/PasswordGeneratorDialogFragment.kt
index af344bd4..26b6190f 100644
--- a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/PasswordGeneratorDialogFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/PasswordGeneratorDialogFragment.kt
@@ -4,7 +4,6 @@
*/
package com.zeapo.pwdstore.ui.dialogs
-import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.graphics.Typeface
@@ -26,7 +25,7 @@ import com.zeapo.pwdstore.pwgen.PasswordGenerator.setPrefs
class PasswordGeneratorDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = MaterialAlertDialogBuilder(requireContext())
- val callingActivity: Activity = requireActivity()
+ val callingActivity = requireActivity()
val inflater = callingActivity.layoutInflater
val view = inflater.inflate(R.layout.fragment_pwgen, null)
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
@@ -49,8 +48,8 @@ class PasswordGeneratorDialogFragment : DialogFragment() {
val edit = callingActivity.findViewById<EditText>(R.id.crypto_password_edit)
edit.setText(passwordText.text)
}
- builder.setNegativeButton(resources.getString(R.string.dialog_cancel)) { _, _ -> }
- builder.setNeutralButton(resources.getString(R.string.pwgen_generate), null)
+ 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.pwgen_title)).create()
dialog.setOnShowListener {
setPreferences()
@@ -60,7 +59,7 @@ class PasswordGeneratorDialogFragment : DialogFragment() {
Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show()
passwordText.text = ""
}
- dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener {
+ dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
setPreferences()
try {
passwordText.text = generate(callingActivity.applicationContext)[0]
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/XkPasswordGeneratorDialogFragment.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/XkPasswordGeneratorDialogFragment.kt
new file mode 100644
index 00000000..f42b6536
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/XkPasswordGeneratorDialogFragment.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.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.CheckBox
+import android.widget.EditText
+import android.widget.Spinner
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.widget.AppCompatEditText
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.pwgen.PasswordGenerator
+import com.zeapo.pwdstore.pwgenxkpwd.CapsType
+import com.zeapo.pwdstore.pwgenxkpwd.PasswordBuilder
+import timber.log.Timber
+
+/** A placeholder fragment containing a simple view. */
+class XkPasswordGeneratorDialogFragment : DialogFragment() {
+
+ private lateinit var editSeparator: AppCompatEditText
+ private lateinit var editNumWords: AppCompatEditText
+ private lateinit var cbSymbols: CheckBox
+ private lateinit var spinnerCapsType: Spinner
+ private lateinit var cbNumbers: CheckBox
+ private lateinit var prefs: SharedPreferences
+ private lateinit var spinnerNumbersCount: Spinner
+ private lateinit var spinnerSymbolsCount: Spinner
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val builder = MaterialAlertDialogBuilder(requireContext())
+ val callingActivity = requireActivity()
+ val inflater = callingActivity.layoutInflater
+ val view = inflater.inflate(R.layout.fragment_xkpwgen, null)
+
+ val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
+
+ builder.setView(view)
+
+ prefs = callingActivity.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
+
+ cbNumbers = view.findViewById<CheckBox>(R.id.xknumerals)
+ cbNumbers.isChecked = prefs.getBoolean(PREF_KEY_USE_NUMERALS, false)
+
+ spinnerNumbersCount = view.findViewById<Spinner>(R.id.xk_numbers_count)
+
+ val storedNumbersCount = prefs.getInt(PREF_KEY_NUMBERS_COUNT, 0)
+ spinnerNumbersCount.setSelection(storedNumbersCount)
+
+ cbSymbols = view.findViewById<CheckBox>(R.id.xksymbols)
+ cbSymbols.isChecked = prefs.getBoolean(PREF_KEY_USE_SYMBOLS, false) != false
+ spinnerSymbolsCount = view.findViewById<Spinner>(R.id.xk_symbols_count)
+ val symbolsCount = prefs.getInt(PREF_KEY_SYMBOLS_COUNT, 0)
+ spinnerSymbolsCount.setSelection(symbolsCount)
+
+ val previousStoredCapStyle: String = try {
+ prefs.getString(PREF_KEY_CAPITALS_STYLE, DEFAULT_CAPS_STYLE)!!
+ } catch (e: Exception) {
+ Timber.tag("xkpw").e(e)
+ DEFAULT_CAPS_STYLE
+ }
+ spinnerCapsType = view.findViewById<Spinner>(R.id.xkCapType)
+
+ val lastCapitalsStyleIndex: Int
+
+ lastCapitalsStyleIndex = try {
+ CapsType.valueOf(previousStoredCapStyle).ordinal
+ } catch (e: Exception) {
+ Timber.tag("xkpw").e(e)
+ DEFAULT_CAPS_INDEX
+ }
+ spinnerCapsType.setSelection(lastCapitalsStyleIndex)
+
+ editNumWords = view.findViewById<AppCompatEditText>(R.id.xk_num_words)
+ editNumWords.setText(prefs.getString(PREF_KEY_NUM_WORDS, DEFAULT_NUMBER_OF_WORDS))
+
+ editSeparator = view.findViewById<AppCompatEditText>(R.id.xk_separator)
+ editSeparator.setText(prefs.getString(PREF_KEY_SEPARATOR, DEFAULT_WORD_SEPARATOR))
+
+ val passwordText: AppCompatTextView = view.findViewById(R.id.xkPasswordText)
+ passwordText.typeface = monoTypeface
+
+ builder.setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ ->
+ setPreferences()
+ val edit = callingActivity.findViewById<EditText>(R.id.crypto_password_edit)
+ edit.setText(passwordText.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()
+
+ dialog.setOnShowListener {
+ setPreferences()
+ makeAndSetPassword(passwordText)
+
+ dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
+ setPreferences()
+ makeAndSetPassword(passwordText)
+ }
+ }
+ return dialog
+ }
+
+ private fun makeAndSetPassword(passwordText: AppCompatTextView) {
+ try {
+ passwordText.text = PasswordBuilder(requireContext())
+ .setNumberOfWords(Integer.valueOf(editNumWords.text.toString()))
+ .setMinimumWordLength(DEFAULT_MIN_WORD_LENGTH)
+ .setMaximumWordLength(DEFAULT_MAX_WORD_LENGTH)
+ .setSeparator(editSeparator.text.toString())
+ .appendNumbers(if (cbNumbers.isChecked) Integer.parseInt(spinnerNumbersCount.selectedItem as String) else 0)
+ .appendSymbols(if (cbSymbols.isChecked) Integer.parseInt(spinnerSymbolsCount.selectedItem as String) else 0)
+ .setCapitalization(CapsType.valueOf(spinnerCapsType.selectedItem.toString())).create()
+ } catch (e: PasswordGenerator.PasswordGeneratorExeption) {
+ Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show()
+ Timber.tag("xkpw").e(e, "failure generating xkpasswd")
+ passwordText.text = FALLBACK_ERROR_PASS
+ }
+ }
+
+ private fun setPreferences() {
+ prefs.edit().putBoolean(PREF_KEY_USE_NUMERALS, cbNumbers.isChecked)
+ .putBoolean(PREF_KEY_USE_SYMBOLS, cbSymbols.isChecked)
+ .putString(PREF_KEY_CAPITALS_STYLE, spinnerCapsType.selectedItem.toString())
+ .putString(PREF_KEY_NUM_WORDS, editNumWords.text.toString())
+ .putString(PREF_KEY_SEPARATOR, editSeparator.text.toString())
+ .putInt(PREF_KEY_NUMBERS_COUNT, Integer.parseInt(spinnerNumbersCount.selectedItem as String) - 1)
+ .putInt(PREF_KEY_SYMBOLS_COUNT, Integer.parseInt(spinnerSymbolsCount.selectedItem as String) - 1)
+ .apply()
+ }
+
+ companion object {
+ const val PREF_KEY_USE_NUMERALS = "pref_key_use_numerals"
+ const val PREF_KEY_USE_SYMBOLS = "pref_key_use_symbols"
+ 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_NUMBERS_COUNT = "pref_key_xkpwgen_numbers_count"
+ const val PREF_KEY_SYMBOLS_COUNT = "pref_key_symbols_count"
+ val DEFAULT_CAPS_STYLE = CapsType.Sentencecase.name
+ val DEFAULT_CAPS_INDEX = CapsType.Sentencecase.ordinal
+ const val DEFAULT_NUMBER_OF_WORDS = "3"
+ const val DEFAULT_WORD_SEPARATOR = "."
+ const val DEFAULT_MIN_WORD_LENGTH = 3
+ const val DEFAULT_MAX_WORD_LENGTH = 9
+ const val FALLBACK_ERROR_PASS = "42"
+ }
+}