diff options
4 files changed, 60 insertions, 29 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt index e8e1f565..74e14b91 100644 --- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillDecryptActivity.kt @@ -11,6 +11,7 @@ import android.content.IntentSender import android.os.Build import android.os.Bundle import android.view.autofill.AutofillManager +import androidx.activity.viewModels import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope @@ -21,6 +22,7 @@ import app.passwordstore.util.autofill.AutofillPreferences import app.passwordstore.util.autofill.AutofillResponseBuilder import app.passwordstore.util.autofill.DirectoryStructure import app.passwordstore.util.extensions.asLog +import app.passwordstore.util.viewmodel.PasswordViewModel import com.github.androidpasswordstore.autofillparser.AutofillAction import com.github.androidpasswordstore.autofillparser.Credentials import com.github.michaelbull.result.getOrElse @@ -77,6 +79,7 @@ class AutofillDecryptActivity : AppCompatActivity() { } } + private val viewModel: PasswordViewModel by viewModels() @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory @Inject lateinit var repository: CryptoRepository @@ -105,10 +108,8 @@ class AutofillDecryptActivity : AppCompatActivity() { val dialog = PasswordDialog() lifecycleScope.launch { withContext(Dispatchers.Main) { - dialog.password.collectLatest { value -> - if (value != null) { - decrypt(File(filePath), clientState, action, value) - } + viewModel.password.collectLatest { value -> + decrypt(File(filePath), clientState, action, value) } } } diff --git a/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt b/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt index ce14c64d..72893089 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/DecryptActivity.kt @@ -9,6 +9,7 @@ import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuItem +import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import app.passwordstore.R import app.passwordstore.data.crypto.CryptoRepository @@ -22,6 +23,7 @@ import app.passwordstore.util.extensions.unsafeLazy import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.settings.Constants import app.passwordstore.util.settings.PreferenceKeys +import app.passwordstore.util.viewmodel.PasswordViewModel import com.github.michaelbull.result.Err import com.github.michaelbull.result.Ok import com.github.michaelbull.result.runCatching @@ -45,6 +47,7 @@ import logcat.logcat @AndroidEntryPoint class DecryptActivity : BasePgpActivity() { + private val viewModel: PasswordViewModel by viewModels() private val binding by viewBinding(DecryptLayoutBinding::inflate) private val relativeParentPath by unsafeLazy { getParentPath(fullPath, repoPath) } @Inject lateinit var passwordEntryFactory: PasswordEntry.Factory @@ -67,7 +70,7 @@ class DecryptActivity : BasePgpActivity() { true } } - askPassphrase(isError = false) + askPassphrase() } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -138,34 +141,36 @@ class DecryptActivity : BasePgpActivity() { ) } - private fun askPassphrase(isError: Boolean) { + private fun askPassphrase() { if (retries < MAX_RETRIES) { retries += 1 } else { finish() } - val dialog = PasswordDialog() - if (isError) { - dialog.setError() - } + showPasswordDialog(isError = false) lifecycleScope.launch(dispatcherProvider.main()) { - dialog.password.collectLatest { value -> - if (value != null) { - when (val result = decryptWithPassphrase(value)) { - is Ok -> { - val entry = passwordEntryFactory.create(result.value.toByteArray()) - passwordEntry = entry - createPasswordUI(entry) - startAutoDismissTimer() - } - is Err -> { - logcat(ERROR) { result.error.stackTraceToString() } - askPassphrase(isError = true) - } + viewModel.password.collectLatest { value -> + when (val result = decryptWithPassphrase(value)) { + is Ok -> { + val entry = passwordEntryFactory.create(result.value.toByteArray()) + passwordEntry = entry + createPasswordUI(entry) + startAutoDismissTimer() + } + is Err -> { + logcat(ERROR) { result.error.stackTraceToString() } + showPasswordDialog(isError = true) } } } } + } + + private fun showPasswordDialog(isError: Boolean) { + val dialog = PasswordDialog() + if (isError) { + dialog.setError() + } dialog.show(supportFragmentManager, "PASSWORD_DIALOG") } diff --git a/app/src/main/java/app/passwordstore/ui/crypto/PasswordDialog.kt b/app/src/main/java/app/passwordstore/ui/crypto/PasswordDialog.kt index 25aa18e4..1a50be33 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/PasswordDialog.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/PasswordDialog.kt @@ -12,22 +12,20 @@ import android.view.KeyEvent import android.view.WindowManager import androidx.core.widget.doOnTextChanged import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels import app.passwordstore.R import app.passwordstore.databinding.DialogPasswordEntryBinding import app.passwordstore.util.extensions.finish import app.passwordstore.util.extensions.unsafeLazy +import app.passwordstore.util.viewmodel.PasswordViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update /** [DialogFragment] to request a password from the user and forward it along. */ class PasswordDialog : DialogFragment() { + private val viewModel: PasswordViewModel by activityViewModels() private val binding by unsafeLazy { DialogPasswordEntryBinding.inflate(layoutInflater) } private var isError: Boolean = false - private val _password = MutableStateFlow<String?>(null) - val password = _password.asStateFlow() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = MaterialAlertDialogBuilder(requireContext()) @@ -66,7 +64,7 @@ class PasswordDialog : DialogFragment() { } private fun setPasswordAndDismiss() { - _password.update { binding.passwordEditText.text.toString() } + viewModel.setPassword(binding.passwordEditText.text.toString()) dismissAllowingStateLoss() } } diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/PasswordViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/PasswordViewModel.kt new file mode 100644 index 00000000..9cd5a597 --- /dev/null +++ b/app/src/main/java/app/passwordstore/util/viewmodel/PasswordViewModel.kt @@ -0,0 +1,27 @@ +/* + * Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ + +package app.passwordstore.util.viewmodel + +import androidx.lifecycle.ViewModel +import app.passwordstore.ui.crypto.PasswordDialog +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.dropWhile +import kotlinx.coroutines.flow.update + +/** + * ViewModel for classes that use [PasswordDialog] for getting user input. [PasswordDialog] will + * ensure that it retrieves an instance of this [ViewModel] from the Activity scope and will post + * values into [PasswordViewModel.password]. + */ +class PasswordViewModel : ViewModel() { + private val _password = MutableStateFlow("") + val password = _password.asStateFlow().dropWhile(String::isBlank) + + fun setPassword(pass: String) { + _password.update { pass } + } +} |