diff options
| author | Harsh Shandilya <me@msfjarvis.dev> | 2022-07-15 00:53:48 +0530 |
|---|---|---|
| committer | Harsh Shandilya <me@msfjarvis.dev> | 2022-07-15 01:13:47 +0530 |
| commit | 549ee790d3e52bc62565ddf92e6a556e98b5195e (patch) | |
| tree | d5758e5eb80093704e683c8da926838e18182588 /app/src/main/java/dev/msfjarvis/aps/ui/dialogs | |
| parent | 010c6e227c9cc27f4d01bc912311f977b2aeb3a7 (diff) | |
| download | HardenedPass-549ee790d3e52bc62565ddf92e6a556e98b5195e.tar.gz HardenedPass-549ee790d3e52bc62565ddf92e6a556e98b5195e.zip | |
all: re-do package structure yet again
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/ui/dialogs')
6 files changed, 0 insertions, 639 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/BasicBottomSheet.kt b/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/BasicBottomSheet.kt deleted file mode 100644 index 53d9a201..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/BasicBottomSheet.kt +++ /dev/null @@ -1,166 +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.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.ViewTreeObserver -import android.widget.FrameLayout -import androidx.annotation.StringRes -import androidx.core.view.isVisible -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.databinding.BasicBottomSheetBinding -import dev.msfjarvis.aps.util.extensions.viewBinding - -/** - * [BottomSheetDialogFragment] that exposes a simple [androidx.appcompat.app.AlertDialog] like API - * through [BasicBottomSheet.Builder] to create a similar UI, just at the bottom of the screen. - */ -class BasicBottomSheet -private constructor( - val title: String?, - val message: String, - val positiveButtonLabel: String?, - val negativeButtonLabel: String?, - val positiveButtonClickListener: View.OnClickListener?, - val negativeButtonClickListener: View.OnClickListener?, -) : BottomSheetDialogFragment() { - - private val binding by viewBinding(BasicBottomSheetBinding::bind) - - private var behavior: BottomSheetBehavior<FrameLayout>? = null - private val bottomSheetCallback = - object : BottomSheetBehavior.BottomSheetCallback() { - override fun onSlide(bottomSheet: View, slideOffset: Float) {} - - override fun onStateChanged(bottomSheet: View, newState: Int) { - if (newState == BottomSheetBehavior.STATE_COLLAPSED) { - dismiss() - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - if (savedInstanceState != null) dismiss() - return layoutInflater.inflate(R.layout.basic_bottom_sheet, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - view.viewTreeObserver.addOnGlobalLayoutListener( - object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - view.viewTreeObserver.removeOnGlobalLayoutListener(this) - val dialog = dialog as BottomSheetDialog? ?: return - behavior = dialog.behavior - behavior?.apply { - state = BottomSheetBehavior.STATE_EXPANDED - peekHeight = 0 - addBottomSheetCallback(bottomSheetCallback) - } - if (!title.isNullOrEmpty()) { - binding.bottomSheetTitle.isVisible = true - binding.bottomSheetTitle.text = title - } - binding.bottomSheetMessage.text = message - if (positiveButtonClickListener != null) { - positiveButtonLabel?.let { buttonLbl -> binding.bottomSheetOkButton.text = buttonLbl } - binding.bottomSheetOkButton.isVisible = true - binding.bottomSheetOkButton.setOnClickListener { - positiveButtonClickListener.onClick(it) - dismiss() - } - } - if (negativeButtonClickListener != null) { - binding.bottomSheetCancelButton.isVisible = true - negativeButtonLabel?.let { buttonLbl -> - binding.bottomSheetCancelButton.text = buttonLbl - } - binding.bottomSheetCancelButton.setOnClickListener { - negativeButtonClickListener.onClick(it) - dismiss() - } - } - } - } - ) - } - - override fun dismiss() { - super.dismiss() - behavior?.removeBottomSheetCallback(bottomSheetCallback) - } - - class Builder(val context: Context) { - - private var title: String? = null - private var message: String? = null - private var positiveButtonLabel: String? = null - private var negativeButtonLabel: String? = null - private var positiveButtonClickListener: View.OnClickListener? = null - private var negativeButtonClickListener: View.OnClickListener? = null - - fun setTitleRes(@StringRes titleRes: Int): Builder { - this.title = context.resources.getString(titleRes) - return this - } - - fun setTitle(title: String): Builder { - this.title = title - return this - } - - fun setMessageRes(@StringRes messageRes: Int): Builder { - this.message = context.resources.getString(messageRes) - return this - } - - fun setMessage(message: String): Builder { - this.message = message - return this - } - - fun setPositiveButtonClickListener( - buttonLabel: String? = null, - listener: View.OnClickListener - ): Builder { - this.positiveButtonClickListener = listener - this.positiveButtonLabel = buttonLabel - return this - } - - fun setNegativeButtonClickListener( - buttonLabel: String? = null, - listener: View.OnClickListener - ): Builder { - this.negativeButtonClickListener = listener - this.negativeButtonLabel = buttonLabel - return this - } - - fun build(): BasicBottomSheet { - require(message != null) { "Message needs to be set" } - return BasicBottomSheet( - title, - message!!, - positiveButtonLabel, - negativeButtonLabel, - positiveButtonClickListener, - negativeButtonClickListener - ) - } - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt deleted file mode 100644 index 22f991c5..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/DicewarePasswordGeneratorDialogFragment.kt +++ /dev/null @@ -1,90 +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.AlertDialog -import android.app.Dialog -import android.content.SharedPreferences -import android.graphics.Typeface -import android.os.Bundle -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.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.databinding.FragmentPwgenDicewareBinding -import dev.msfjarvis.aps.injection.prefs.PasswordGeneratorPreferences -import dev.msfjarvis.aps.passgen.diceware.DicewarePassphraseGenerator -import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity -import dev.msfjarvis.aps.util.extensions.getString -import dev.msfjarvis.aps.util.settings.PreferenceKeys.DICEWARE_LENGTH -import dev.msfjarvis.aps.util.settings.PreferenceKeys.DICEWARE_SEPARATOR -import javax.inject.Inject -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach -import reactivecircus.flowbinding.android.widget.afterTextChanges - -@AndroidEntryPoint -class DicewarePasswordGeneratorDialogFragment : DialogFragment() { - - @Inject lateinit var dicewareGenerator: DicewarePassphraseGenerator - @Inject @PasswordGeneratorPreferences lateinit var prefs: SharedPreferences - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = MaterialAlertDialogBuilder(requireContext()) - - val binding = FragmentPwgenDicewareBinding.inflate(layoutInflater) - builder.setView(binding.root) - - binding.passwordSeparatorText.setText(prefs.getString(DICEWARE_SEPARATOR) ?: "-") - binding.passwordLengthText.setText(prefs.getInt(DICEWARE_LENGTH, 5).toString()) - binding.passwordText.typeface = Typeface.MONOSPACE - - merge( - binding.passwordLengthText.afterTextChanges(), - binding.passwordSeparatorText.afterTextChanges(), - ) - .onEach { generatePassword(binding) } - .launchIn(lifecycleScope) - return builder - .run { - setTitle(R.string.pwgen_title) - setPositiveButton(R.string.dialog_ok) { _, _ -> - setFragmentResult( - PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY, - bundleOf(PasswordCreationActivity.RESULT to "${binding.passwordText.text}") - ) - } - setNeutralButton(R.string.dialog_cancel) { _, _ -> } - setNegativeButton(R.string.pwgen_generate, null) - create() - } - .apply { - setOnShowListener { - generatePassword(binding) - getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener { generatePassword(binding) } - } - } - } - - private fun generatePassword(binding: FragmentPwgenDicewareBinding) { - val length = binding.passwordLengthText.text?.toString()?.toIntOrNull() ?: 5 - val separator = binding.passwordSeparatorText.text?.toString()?.getOrNull(0) ?: '-' - setPreferences(length, separator) - binding.passwordText.text = dicewareGenerator.generatePassphrase(length, separator) - } - - private fun setPreferences(length: Int, separator: Char) { - prefs.edit { - putInt(DICEWARE_LENGTH, length) - putString(DICEWARE_SEPARATOR, separator.toString()) - } - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/FolderCreationDialogFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/FolderCreationDialogFragment.kt deleted file mode 100644 index 9b7b3e21..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/FolderCreationDialogFragment.kt +++ /dev/null @@ -1,110 +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.Intent -import android.os.Bundle -import android.view.WindowManager -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import androidx.lifecycle.lifecycleScope -import com.google.android.material.checkbox.MaterialCheckBox -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.textfield.TextInputEditText -import com.google.android.material.textfield.TextInputLayout -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.data.repo.PasswordRepository -import dev.msfjarvis.aps.ui.crypto.BasePgpActivity -import dev.msfjarvis.aps.ui.crypto.GetKeyIdsActivity -import dev.msfjarvis.aps.ui.passwords.PasswordStore -import dev.msfjarvis.aps.util.extensions.commitChange -import java.io.File -import kotlinx.coroutines.launch -import me.msfjarvis.openpgpktx.util.OpenPgpApi - -class FolderCreationDialogFragment : DialogFragment() { - - private lateinit var newFolder: File - - private val keySelectAction = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode == AppCompatActivity.RESULT_OK) { - result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds -> - val gpgIdentifierFile = File(newFolder, ".gpg-id") - gpgIdentifierFile.writeText(keyIds.joinToString("\n")) - if (PasswordRepository.repository != null) { - lifecycleScope.launch { - val repoPath = PasswordRepository.getRepositoryDirectory().absolutePath - requireActivity() - .commitChange( - getString( - R.string.git_commit_gpg_id, - BasePgpActivity.getLongName( - gpgIdentifierFile.parentFile!!.absolutePath, - repoPath, - gpgIdentifierFile.name - ) - ), - ) - dismiss() - } - } - } - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val alertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) - alertDialogBuilder.setTitle(R.string.title_create_folder) - alertDialogBuilder.setView(R.layout.folder_dialog_fragment) - alertDialogBuilder.setPositiveButton(getString(R.string.button_create), null) - alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dismiss() } - val dialog = alertDialogBuilder.create() - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) - dialog.setOnShowListener { - dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - createDirectory(requireArguments().getString(CURRENT_DIR_EXTRA)!!) - } - } - return dialog - } - - private fun createDirectory(currentDir: String) { - val dialog = requireDialog() - val folderNameView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text) - val folderNameViewContainer = dialog.findViewById<TextInputLayout>(R.id.folder_name_container) - newFolder = File("$currentDir/${folderNameView.text}") - folderNameViewContainer.error = - when { - newFolder.isFile -> getString(R.string.folder_creation_err_file_exists) - newFolder.isDirectory -> getString(R.string.folder_creation_err_folder_exists) - else -> null - } - if (folderNameViewContainer.error != null) return - newFolder.mkdirs() - (requireActivity() as PasswordStore).refreshPasswordList(newFolder) - if (dialog.findViewById<MaterialCheckBox>(R.id.set_gpg_key).isChecked) { - keySelectAction.launch(Intent(requireContext(), GetKeyIdsActivity::class.java)) - return - } else { - dismiss() - } - } - - companion object { - - private const val CURRENT_DIR_EXTRA = "CURRENT_DIRECTORY" - fun newInstance(startingDirectory: String): FolderCreationDialogFragment { - val extras = bundleOf(CURRENT_DIR_EXTRA to startingDirectory) - val fragment = FolderCreationDialogFragment() - fragment.arguments = extras - return fragment - } - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/ItemCreationBottomSheet.kt b/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/ItemCreationBottomSheet.kt deleted file mode 100644 index af2a5f19..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/ItemCreationBottomSheet.kt +++ /dev/null @@ -1,77 +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.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.ViewTreeObserver -import android.widget.FrameLayout -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.ui.passwords.PasswordFragment.Companion.ACTION_FOLDER -import dev.msfjarvis.aps.ui.passwords.PasswordFragment.Companion.ACTION_KEY -import dev.msfjarvis.aps.ui.passwords.PasswordFragment.Companion.ACTION_PASSWORD -import dev.msfjarvis.aps.ui.passwords.PasswordFragment.Companion.ITEM_CREATION_REQUEST_KEY - -class ItemCreationBottomSheet : BottomSheetDialogFragment() { - - private var behavior: BottomSheetBehavior<FrameLayout>? = null - private val bottomSheetCallback = - object : BottomSheetBehavior.BottomSheetCallback() { - override fun onSlide(bottomSheet: View, slideOffset: Float) {} - - override fun onStateChanged(bottomSheet: View, newState: Int) { - if (newState == BottomSheetBehavior.STATE_COLLAPSED) { - dismiss() - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - if (savedInstanceState != null) dismiss() - return inflater.inflate(R.layout.item_create_sheet, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - view.viewTreeObserver.addOnGlobalLayoutListener( - object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - view.viewTreeObserver.removeOnGlobalLayoutListener(this) - val dialog = dialog as BottomSheetDialog? ?: return - behavior = dialog.behavior - behavior?.apply { - state = BottomSheetBehavior.STATE_EXPANDED - peekHeight = 0 - addBottomSheetCallback(bottomSheetCallback) - } - dialog.findViewById<View>(R.id.create_folder)?.setOnClickListener { - setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_FOLDER)) - dismiss() - } - dialog.findViewById<View>(R.id.create_password)?.setOnClickListener { - setFragmentResult(ITEM_CREATION_REQUEST_KEY, bundleOf(ACTION_KEY to ACTION_PASSWORD)) - dismiss() - } - } - } - ) - } - - override fun dismiss() { - super.dismiss() - behavior?.removeBottomSheetCallback(bottomSheetCallback) - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/OtpImportDialogFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/OtpImportDialogFragment.kt deleted file mode 100644 index 1670d263..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/OtpImportDialogFragment.kt +++ /dev/null @@ -1,47 +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.net.Uri -import android.os.Bundle -import android.view.WindowManager -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.setFragmentResult -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dev.msfjarvis.aps.databinding.FragmentManualOtpEntryBinding -import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity - -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.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) - 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() - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/PasswordGeneratorDialogFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/PasswordGeneratorDialogFragment.kt deleted file mode 100644 index e14076b1..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/ui/dialogs/PasswordGeneratorDialogFragment.kt +++ /dev/null @@ -1,149 +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.AlertDialog -import android.app.Dialog -import android.content.Context -import android.graphics.Typeface -import android.os.Bundle -import android.widget.CheckBox -import android.widget.EditText -import android.widget.Toast -import androidx.annotation.IdRes -import androidx.appcompat.widget.AppCompatTextView -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.getOrElse -import com.github.michaelbull.result.runCatching -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.databinding.FragmentPwgenBinding -import dev.msfjarvis.aps.passgen.random.MaxIterationsExceededException -import dev.msfjarvis.aps.passgen.random.NoCharactersIncludedException -import dev.msfjarvis.aps.passgen.random.PasswordGenerator -import dev.msfjarvis.aps.passgen.random.PasswordLengthTooShortException -import dev.msfjarvis.aps.passgen.random.PasswordOption -import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity -import dev.msfjarvis.aps.util.settings.PreferenceKeys -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach -import reactivecircus.flowbinding.android.widget.afterTextChanges -import reactivecircus.flowbinding.android.widget.checkedChanges - -class PasswordGeneratorDialogFragment : DialogFragment() { - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val prefs = requireContext().getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE) - val builder = MaterialAlertDialogBuilder(requireContext()) - - val binding = FragmentPwgenBinding.inflate(layoutInflater) - builder.setView(binding.root) - - binding.numerals.isChecked = !prefs.getBoolean(PasswordOption.NoDigits.key, false) - binding.symbols.isChecked = prefs.getBoolean(PasswordOption.AtLeastOneSymbol.key, false) - binding.uppercase.isChecked = !prefs.getBoolean(PasswordOption.NoUppercaseLetters.key, false) - binding.lowercase.isChecked = !prefs.getBoolean(PasswordOption.NoLowercaseLetters.key, false) - 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 = Typeface.MONOSPACE - - merge( - binding.numerals.checkedChanges().skipInitialValue(), - binding.symbols.checkedChanges().skipInitialValue(), - binding.uppercase.checkedChanges().skipInitialValue(), - binding.lowercase.checkedChanges().skipInitialValue(), - binding.ambiguous.checkedChanges().skipInitialValue(), - binding.pronounceable.checkedChanges().skipInitialValue(), - binding.lengthNumber.afterTextChanges().skipInitialValue(), - ) - .onEach { generate(binding.passwordText) } - .launchIn(lifecycleScope) - - return builder - .run { - setTitle(R.string.pwgen_title) - setPositiveButton(R.string.dialog_ok) { _, _ -> - setFragmentResult( - PasswordCreationActivity.PASSWORD_RESULT_REQUEST_KEY, - bundleOf(PasswordCreationActivity.RESULT to "${binding.passwordText.text}") - ) - } - setNeutralButton(R.string.dialog_cancel) { _, _ -> } - setNegativeButton(R.string.pwgen_generate, null) - create() - } - .apply { - setOnShowListener { - generate(binding.passwordText) - getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener { - generate(binding.passwordText) - } - } - } - } - - private fun generate(passwordField: AppCompatTextView) { - val passwordOptions = getSelectedOptions() - val passwordLength = getLength() - setPrefs(requireContext(), passwordOptions, passwordLength) - passwordField.text = - runCatching { PasswordGenerator.generate(passwordOptions, passwordLength) } - .getOrElse { exception -> - val errorText = - when (exception) { - is MaxIterationsExceededException -> - requireContext().getString(R.string.pwgen_max_iterations_exceeded) - is NoCharactersIncludedException -> - requireContext().getString(R.string.pwgen_no_chars_error) - is PasswordLengthTooShortException -> - requireContext().getString(R.string.pwgen_length_too_short_error) - else -> requireContext().getString(R.string.pwgen_some_error_occurred) - } - Toast.makeText(requireActivity(), errorText, Toast.LENGTH_SHORT).show() - "" - } - } - - private fun isChecked(@IdRes id: Int): Boolean { - return requireDialog().findViewById<CheckBox>(id).isChecked - } - - private fun getSelectedOptions(): List<PasswordOption> { - return listOfNotNull( - PasswordOption.NoDigits.takeIf { !isChecked(R.id.numerals) }, - PasswordOption.AtLeastOneSymbol.takeIf { isChecked(R.id.symbols) }, - PasswordOption.NoUppercaseLetters.takeIf { !isChecked(R.id.uppercase) }, - PasswordOption.NoAmbiguousCharacters.takeIf { !isChecked(R.id.ambiguous) }, - PasswordOption.FullyRandom.takeIf { !isChecked(R.id.pronounceable) }, - PasswordOption.NoLowercaseLetters.takeIf { !isChecked(R.id.lowercase) } - ) - } - - private fun getLength(): Int { - val lengthText = requireDialog().findViewById<EditText>(R.id.lengthNumber).text.toString() - return lengthText.toIntOrNull()?.takeIf { it >= 0 } ?: PasswordGenerator.DEFAULT_LENGTH - } - - /** - * Enables the [PasswordOption]s in [options] and sets [targetLength] as the length for generated - * passwords. - */ - private fun setPrefs(ctx: Context, options: List<PasswordOption>, targetLength: Int): Boolean { - ctx.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE).edit { - for (possibleOption in PasswordOption.values()) putBoolean( - possibleOption.key, - possibleOption in options - ) - putInt("length", targetLength) - } - return true - } -} |
