mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-08-30 13:57:47 +00:00
Implement manual TOTP import and cleanup password generators (#1320)
This commit is contained in:
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Invalid `.gpg-id` files can now be fixed automatically by deleting them and then trying to create a new password.
|
- Invalid `.gpg-id` files can now be fixed automatically by deleting them and then trying to create a new password.
|
||||||
- Suggest users to re-clone repository when it is deemed to be broken
|
- Suggest users to re-clone repository when it is deemed to be broken
|
||||||
- Allow doing a merge instead of a rebase when pulling or syncing
|
- Allow doing a merge instead of a rebase when pulling or syncing
|
||||||
|
- Add support for manually providing TOTP parameters
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ import dev.msfjarvis.aps.R
|
|||||||
import dev.msfjarvis.aps.data.password.PasswordEntry
|
import dev.msfjarvis.aps.data.password.PasswordEntry
|
||||||
import dev.msfjarvis.aps.data.repo.PasswordRepository
|
import dev.msfjarvis.aps.data.repo.PasswordRepository
|
||||||
import dev.msfjarvis.aps.databinding.PasswordCreationActivityBinding
|
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.PasswordGeneratorDialogFragment
|
||||||
import dev.msfjarvis.aps.ui.dialogs.XkPasswordGeneratorDialogFragment
|
import dev.msfjarvis.aps.ui.dialogs.XkPasswordGeneratorDialogFragment
|
||||||
import dev.msfjarvis.aps.util.autofill.AutofillPreferences
|
import dev.msfjarvis.aps.util.autofill.AutofillPreferences
|
||||||
@@ -145,12 +146,30 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||||||
setContentView(root)
|
setContentView(root)
|
||||||
generatePassword.setOnClickListener { generatePassword() }
|
generatePassword.setOnClickListener { generatePassword() }
|
||||||
otpImportButton.setOnClickListener {
|
otpImportButton.setOnClickListener {
|
||||||
otpImportAction.launch(IntentIntegrator(this@PasswordCreationActivity)
|
supportFragmentManager.setFragmentResultListener(OTP_RESULT_REQUEST_KEY, this@PasswordCreationActivity) { requestKey, bundle ->
|
||||||
.setOrientationLocked(false)
|
if (requestKey == OTP_RESULT_REQUEST_KEY) {
|
||||||
.setBeepEnabled(false)
|
val contents = bundle.getString(RESULT)
|
||||||
.setDesiredBarcodeFormats(QR_CODE)
|
val currentExtras = binding.extraContent.text.toString()
|
||||||
.createScanIntent()
|
if (currentExtras.isNotEmpty() && currentExtras.last() != '\n')
|
||||||
)
|
binding.extraContent.append("\n$contents")
|
||||||
|
else
|
||||||
|
binding.extraContent.append(contents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val items = arrayOf(getString(R.string.otp_import_qr_code), getString(R.string.otp_import_manual_entry))
|
||||||
|
MaterialAlertDialogBuilder(this@PasswordCreationActivity)
|
||||||
|
.setItems(items) { _, index ->
|
||||||
|
if (index == 0) {
|
||||||
|
otpImportAction.launch(IntentIntegrator(this@PasswordCreationActivity)
|
||||||
|
.setOrientationLocked(false)
|
||||||
|
.setBeepEnabled(false)
|
||||||
|
.setDesiredBarcodeFormats(QR_CODE)
|
||||||
|
.createScanIntent())
|
||||||
|
} else if (index == 1) {
|
||||||
|
OtpImportDialogFragment().show(supportFragmentManager, "OtpImport")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryInputLayout.apply {
|
directoryInputLayout.apply {
|
||||||
@@ -249,6 +268,11 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generatePassword() {
|
private fun generatePassword() {
|
||||||
|
supportFragmentManager.setFragmentResultListener(PASSWORD_RESULT_REQUEST_KEY, this) { requestKey, bundle ->
|
||||||
|
if (requestKey == PASSWORD_RESULT_REQUEST_KEY) {
|
||||||
|
binding.password.setText(bundle.getString(RESULT))
|
||||||
|
}
|
||||||
|
}
|
||||||
when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) ?: KEY_PWGEN_TYPE_CLASSIC) {
|
when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) ?: KEY_PWGEN_TYPE_CLASSIC) {
|
||||||
KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment()
|
KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment()
|
||||||
.show(supportFragmentManager, "generator")
|
.show(supportFragmentManager, "generator")
|
||||||
@@ -467,6 +491,9 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
|
|||||||
|
|
||||||
private const val KEY_PWGEN_TYPE_CLASSIC = "classic"
|
private const val KEY_PWGEN_TYPE_CLASSIC = "classic"
|
||||||
private const val KEY_PWGEN_TYPE_XKPASSWD = "xkpasswd"
|
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"
|
||||||
const val RETURN_EXTRA_CREATED_FILE = "CREATED_FILE"
|
const val RETURN_EXTRA_CREATED_FILE = "CREATED_FILE"
|
||||||
const val RETURN_EXTRA_NAME = "NAME"
|
const val RETURN_EXTRA_NAME = "NAME"
|
||||||
const val RETURN_EXTRA_LONG_NAME = "LONG_NAME"
|
const val RETURN_EXTRA_LONG_NAME = "LONG_NAME"
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
package dev.msfjarvis.aps.ui.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.setFragmentResult
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import dev.msfjarvis.aps.R
|
||||||
|
import dev.msfjarvis.aps.databinding.FragmentManualOtpEntryBinding
|
||||||
|
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity
|
||||||
|
import dev.msfjarvis.aps.util.extensions.requestInputFocusOnView
|
||||||
|
|
||||||
|
class OtpImportDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
val binding = FragmentManualOtpEntryBinding.inflate(layoutInflater)
|
||||||
|
builder.setView(binding.root)
|
||||||
|
builder.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
setFragmentResult(
|
||||||
|
PasswordCreationActivity.OTP_RESULT_REQUEST_KEY,
|
||||||
|
bundleOf(
|
||||||
|
PasswordCreationActivity.RESULT to getTOTPUri(binding)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.requestInputFocusOnView<TextInputEditText>(R.id.secret)
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTOTPUri(binding: FragmentManualOtpEntryBinding): String {
|
||||||
|
val secret = binding.secret.text.toString()
|
||||||
|
val account = binding.account.text.toString()
|
||||||
|
if (secret.isBlank()) return ""
|
||||||
|
val builder = Uri.Builder()
|
||||||
|
builder.scheme("otpauth")
|
||||||
|
builder.authority("totp")
|
||||||
|
builder.appendQueryParameter("secret", secret)
|
||||||
|
if (account.isNotBlank()) builder.appendQueryParameter("issuer", account)
|
||||||
|
return builder.build().toString()
|
||||||
|
}
|
||||||
|
}
|
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
package dev.msfjarvis.aps.ui.dialogs
|
package dev.msfjarvis.aps.ui.dialogs
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -14,13 +13,16 @@ import android.widget.CheckBox
|
|||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.appcompat.widget.AppCompatEditText
|
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.setFragmentResult
|
||||||
import com.github.michaelbull.result.getOrElse
|
import com.github.michaelbull.result.getOrElse
|
||||||
import com.github.michaelbull.result.runCatching
|
import com.github.michaelbull.result.runCatching
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.msfjarvis.aps.R
|
import dev.msfjarvis.aps.R
|
||||||
|
import dev.msfjarvis.aps.databinding.FragmentPwgenBinding
|
||||||
|
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity
|
||||||
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator
|
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator
|
||||||
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator.generate
|
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator.generate
|
||||||
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator.setPrefs
|
import dev.msfjarvis.aps.util.pwgen.PasswordGenerator.setPrefs
|
||||||
@@ -30,41 +32,40 @@ import dev.msfjarvis.aps.util.settings.PreferenceKeys
|
|||||||
class PasswordGeneratorDialogFragment : DialogFragment() {
|
class PasswordGeneratorDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
val callingActivity = requireActivity()
|
val callingActivity = requireActivity()
|
||||||
val inflater = callingActivity.layoutInflater
|
val binding = FragmentPwgenBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
|
||||||
val view = inflater.inflate(R.layout.fragment_pwgen, null)
|
|
||||||
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
|
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
|
||||||
val prefs = requireActivity().applicationContext
|
val prefs = requireActivity().applicationContext
|
||||||
.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
view.findViewById<CheckBox>(R.id.numerals)?.isChecked = !prefs.getBoolean(PasswordOption.NoDigits.key, false)
|
builder.setView(binding.root)
|
||||||
view.findViewById<CheckBox>(R.id.symbols)?.isChecked = prefs.getBoolean(PasswordOption.AtLeastOneSymbol.key, false)
|
|
||||||
view.findViewById<CheckBox>(R.id.uppercase)?.isChecked = !prefs.getBoolean(PasswordOption.NoUppercaseLetters.key, false)
|
|
||||||
view.findViewById<CheckBox>(R.id.lowercase)?.isChecked = !prefs.getBoolean(PasswordOption.NoLowercaseLetters.key, false)
|
|
||||||
view.findViewById<CheckBox>(R.id.ambiguous)?.isChecked = !prefs.getBoolean(PasswordOption.NoAmbiguousCharacters.key, false)
|
|
||||||
view.findViewById<CheckBox>(R.id.pronounceable)?.isChecked = !prefs.getBoolean(PasswordOption.FullyRandom.key, true)
|
|
||||||
|
|
||||||
val textView: AppCompatEditText = view.findViewById(R.id.lengthNumber)
|
binding.numerals.isChecked = !prefs.getBoolean(PasswordOption.NoDigits.key, false)
|
||||||
textView.setText(prefs.getInt(PreferenceKeys.LENGTH, 20).toString())
|
binding.symbols.isChecked = prefs.getBoolean(PasswordOption.AtLeastOneSymbol.key, false)
|
||||||
val passwordText: AppCompatTextView = view.findViewById(R.id.passwordText)
|
binding.uppercase.isChecked = !prefs.getBoolean(PasswordOption.NoUppercaseLetters.key, false)
|
||||||
passwordText.typeface = monoTypeface
|
binding.lowercase.isChecked = !prefs.getBoolean(PasswordOption.NoLowercaseLetters.key, false)
|
||||||
return MaterialAlertDialogBuilder(requireContext()).run {
|
binding.ambiguous.isChecked = !prefs.getBoolean(PasswordOption.NoAmbiguousCharacters.key, false)
|
||||||
|
binding.pronounceable.isChecked = !prefs.getBoolean(PasswordOption.FullyRandom.key, true)
|
||||||
|
|
||||||
|
binding.lengthNumber.setText(prefs.getInt(PreferenceKeys.LENGTH, 20).toString())
|
||||||
|
binding.passwordText.typeface = monoTypeface
|
||||||
|
return builder.run {
|
||||||
setTitle(R.string.pwgen_title)
|
setTitle(R.string.pwgen_title)
|
||||||
setView(view)
|
|
||||||
setPositiveButton(R.string.dialog_ok) { _, _ ->
|
setPositiveButton(R.string.dialog_ok) { _, _ ->
|
||||||
val edit = callingActivity.findViewById<EditText>(R.id.password)
|
setFragmentResult(
|
||||||
edit.setText(passwordText.text)
|
PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY,
|
||||||
|
bundleOf(PasswordCreationActivity.RESULT to "${binding.passwordText.text}")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
setNeutralButton(R.string.dialog_cancel) { _, _ -> }
|
setNeutralButton(R.string.dialog_cancel) { _, _ -> }
|
||||||
setNegativeButton(R.string.pwgen_generate, null)
|
setNegativeButton(R.string.pwgen_generate, null)
|
||||||
create()
|
create()
|
||||||
}.apply {
|
}.apply {
|
||||||
setOnShowListener {
|
setOnShowListener {
|
||||||
generate(passwordText)
|
generate(binding.passwordText)
|
||||||
getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
|
getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
|
||||||
generate(passwordText)
|
generate(binding.passwordText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,12 +9,12 @@ import android.content.Context
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.setFragmentResult
|
||||||
import com.github.ajalt.timberkt.Timber.tag
|
import com.github.ajalt.timberkt.Timber.tag
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import com.github.michaelbull.result.getOr
|
import com.github.michaelbull.result.getOr
|
||||||
@@ -22,6 +22,7 @@ import com.github.michaelbull.result.runCatching
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.msfjarvis.aps.R
|
import dev.msfjarvis.aps.R
|
||||||
import dev.msfjarvis.aps.databinding.FragmentXkpwgenBinding
|
import dev.msfjarvis.aps.databinding.FragmentXkpwgenBinding
|
||||||
|
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity
|
||||||
import dev.msfjarvis.aps.util.extensions.getString
|
import dev.msfjarvis.aps.util.extensions.getString
|
||||||
import dev.msfjarvis.aps.util.pwgenxkpwd.CapsType
|
import dev.msfjarvis.aps.util.pwgenxkpwd.CapsType
|
||||||
import dev.msfjarvis.aps.util.pwgenxkpwd.PasswordBuilder
|
import dev.msfjarvis.aps.util.pwgenxkpwd.PasswordBuilder
|
||||||
@@ -29,21 +30,16 @@ import dev.msfjarvis.aps.util.pwgenxkpwd.PasswordBuilder
|
|||||||
/** A placeholder fragment containing a simple view. */
|
/** A placeholder fragment containing a simple view. */
|
||||||
class XkPasswordGeneratorDialogFragment : DialogFragment() {
|
class XkPasswordGeneratorDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
private lateinit var prefs: SharedPreferences
|
|
||||||
private lateinit var binding: FragmentXkpwgenBinding
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
val callingActivity = requireActivity()
|
val callingActivity = requireActivity()
|
||||||
val inflater = callingActivity.layoutInflater
|
val inflater = callingActivity.layoutInflater
|
||||||
binding = FragmentXkpwgenBinding.inflate(inflater)
|
val binding = FragmentXkpwgenBinding.inflate(inflater)
|
||||||
|
|
||||||
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
|
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
|
||||||
|
val prefs = callingActivity.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
builder.setView(binding.root)
|
builder.setView(binding.root)
|
||||||
|
|
||||||
prefs = callingActivity.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
|
|
||||||
|
|
||||||
val previousStoredCapStyle: String = runCatching {
|
val previousStoredCapStyle: String = runCatching {
|
||||||
prefs.getString(PREF_KEY_CAPITALS_STYLE)!!
|
prefs.getString(PREF_KEY_CAPITALS_STYLE)!!
|
||||||
}.getOr(DEFAULT_CAPS_STYLE)
|
}.getOr(DEFAULT_CAPS_STYLE)
|
||||||
@@ -60,9 +56,11 @@ class XkPasswordGeneratorDialogFragment : DialogFragment() {
|
|||||||
binding.xkPasswordText.typeface = monoTypeface
|
binding.xkPasswordText.typeface = monoTypeface
|
||||||
|
|
||||||
builder.setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ ->
|
builder.setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ ->
|
||||||
setPreferences()
|
setPreferences(binding, prefs)
|
||||||
val edit = callingActivity.findViewById<EditText>(R.id.password)
|
setFragmentResult(
|
||||||
edit.setText(binding.xkPasswordText.text)
|
PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY,
|
||||||
|
bundleOf(PasswordCreationActivity.RESULT to "${binding.xkPasswordText.text}")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flip neutral and negative buttons
|
// flip neutral and negative buttons
|
||||||
@@ -72,18 +70,18 @@ class XkPasswordGeneratorDialogFragment : DialogFragment() {
|
|||||||
val dialog = builder.setTitle(this.resources.getString(R.string.xkpwgen_title)).create()
|
val dialog = builder.setTitle(this.resources.getString(R.string.xkpwgen_title)).create()
|
||||||
|
|
||||||
dialog.setOnShowListener {
|
dialog.setOnShowListener {
|
||||||
setPreferences()
|
setPreferences(binding, prefs)
|
||||||
makeAndSetPassword(binding.xkPasswordText)
|
makeAndSetPassword(binding)
|
||||||
|
|
||||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
|
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener {
|
||||||
setPreferences()
|
setPreferences(binding, prefs)
|
||||||
makeAndSetPassword(binding.xkPasswordText)
|
makeAndSetPassword(binding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeAndSetPassword(passwordText: AppCompatTextView) {
|
private fun makeAndSetPassword(binding: FragmentXkpwgenBinding) {
|
||||||
PasswordBuilder(requireContext())
|
PasswordBuilder(requireContext())
|
||||||
.setNumberOfWords(Integer.valueOf(binding.xkNumWords.text.toString()))
|
.setNumberOfWords(Integer.valueOf(binding.xkNumWords.text.toString()))
|
||||||
.setMinimumWordLength(DEFAULT_MIN_WORD_LENGTH)
|
.setMinimumWordLength(DEFAULT_MIN_WORD_LENGTH)
|
||||||
@@ -93,16 +91,16 @@ class XkPasswordGeneratorDialogFragment : DialogFragment() {
|
|||||||
.appendSymbols(binding.xkNumberSymbolMask.text!!.count { c -> c == EXTRA_CHAR_PLACEHOLDER_SYMBOL })
|
.appendSymbols(binding.xkNumberSymbolMask.text!!.count { c -> c == EXTRA_CHAR_PLACEHOLDER_SYMBOL })
|
||||||
.setCapitalization(CapsType.valueOf(binding.xkCapType.selectedItem.toString())).create()
|
.setCapitalization(CapsType.valueOf(binding.xkCapType.selectedItem.toString())).create()
|
||||||
.fold(
|
.fold(
|
||||||
success = { passwordText.text = it },
|
success = { binding.xkPasswordText.text = it },
|
||||||
failure = { e ->
|
failure = { e ->
|
||||||
Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show()
|
||||||
tag("xkpw").e(e, "failure generating xkpasswd")
|
tag("xkpw").e(e, "failure generating xkpasswd")
|
||||||
passwordText.text = FALLBACK_ERROR_PASS
|
binding.xkPasswordText.text = FALLBACK_ERROR_PASS
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setPreferences() {
|
private fun setPreferences(binding: FragmentXkpwgenBinding, prefs: SharedPreferences) {
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
putString(PREF_KEY_CAPITALS_STYLE, binding.xkCapType.selectedItem.toString())
|
putString(PREF_KEY_CAPITALS_STYLE, binding.xkCapType.selectedItem.toString())
|
||||||
putString(PREF_KEY_NUM_WORDS, binding.xkNumWords.text.toString())
|
putString(PREF_KEY_NUM_WORDS, binding.xkNumWords.text.toString())
|
||||||
|
46
app/src/main/res/layout/fragment_manual_otp_entry.xml
Normal file
46
app/src/main/res/layout/fragment_manual_otp_entry.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/secret_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/otp_import_manual_hint_secret"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
app:hintEnabled="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/secret"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/account_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/otp_import_manual_hint_account"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
app:hintEnabled="true"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/secret_layout">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/account"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -398,5 +398,9 @@
|
|||||||
<string name="oreo_autofill_password_fill_and_conditional_save_support">Fill and save passwords (saving requires that no accessibility services are enabled)</string>
|
<string name="oreo_autofill_password_fill_and_conditional_save_support">Fill and save passwords (saving requires that no accessibility services are enabled)</string>
|
||||||
<string name="clear_saved_host_key">Clear saved host key</string>
|
<string name="clear_saved_host_key">Clear saved host key</string>
|
||||||
<string name="clear_saved_host_key_success">Successfully cleared saved host key!</string>
|
<string name="clear_saved_host_key_success">Successfully cleared saved host key!</string>
|
||||||
|
<string name="otp_import_qr_code">Scan QR code</string>
|
||||||
|
<string name="otp_import_manual_entry">Enter manually</string>
|
||||||
|
<string name="otp_import_manual_hint_secret">Secret</string>
|
||||||
|
<string name="otp_import_manual_hint_account">Account</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Reference in New Issue
Block a user