From 26abbbef97f95b874d8d37c2157dc9ffdd707b3f Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Sun, 9 Jul 2023 18:49:28 +0530 Subject: [PATCH] refactor: rework password dialog to avoid memory leak --- .../ui/autofill/AutofillDecryptActivity.kt | 15 +++++++-------- .../passwordstore/ui/crypto/DecryptActivity.kt | 11 ++++++----- .../app/passwordstore/ui/crypto/PasswordDialog.kt | 14 ++++++++------ 3 files changed, 21 insertions(+), 19 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 a7acf11d..98eadb3f 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.fragment.app.setFragmentResultListener import androidx.lifecycle.lifecycleScope import app.passwordstore.R import app.passwordstore.data.crypto.PGPPassphraseCache @@ -35,7 +36,6 @@ import dagger.hilt.android.AndroidEntryPoint import java.io.ByteArrayOutputStream import java.io.File import javax.inject.Inject -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import logcat.LogPriority.ERROR @@ -120,16 +120,15 @@ class AutofillDecryptActivity : BasePGPActivity() { private fun askPassphrase(filePath: String, clientState: Bundle, action: AutofillAction) { val dialog = PasswordDialog() - lifecycleScope.launch { - withContext(dispatcherProvider.main()) { - dialog.password.collectLatest { value -> - if (value != null) { - decryptWithPassphrase(File(filePath), clientState, action, value) - } + dialog.show(supportFragmentManager, "PASSWORD_DIALOG") + dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle -> + if (key == PasswordDialog.PASSWORD_RESULT_KEY) { + val value = bundle.getString(PasswordDialog.PASSWORD_RESULT_KEY)!! + lifecycleScope.launch(dispatcherProvider.main()) { + decryptWithPassphrase(File(filePath), clientState, action, value) } } } - dialog.show(supportFragmentManager, "PASSWORD_DIALOG") } private suspend fun decryptWithPassphrase( 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 f9646186..95a0bbdf 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.fragment.app.setFragmentResultListener import androidx.lifecycle.lifecycleScope import app.passwordstore.R import app.passwordstore.crypto.PGPIdentifier @@ -35,7 +36,6 @@ import java.io.File import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -189,9 +189,11 @@ class DecryptActivity : BasePGPActivity() { if (isError) { dialog.setError() } - lifecycleScope.launch(dispatcherProvider.main()) { - dialog.password.collectLatest { value -> - if (value != null) { + dialog.show(supportFragmentManager, "PASSWORD_DIALOG") + dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle -> + if (key == PasswordDialog.PASSWORD_RESULT_KEY) { + val value = bundle.getString(PasswordDialog.PASSWORD_RESULT_KEY)!! + lifecycleScope.launch(dispatcherProvider.main()) { when (val result = decryptWithPassphrase(value, gpgIdentifiers)) { is Ok -> { val entry = passwordEntryFactory.create(result.value.toByteArray()) @@ -210,7 +212,6 @@ class DecryptActivity : BasePGPActivity() { } } } - dialog.show(supportFragmentManager, "PASSWORD_DIALOG") } private suspend fun decryptWithCachedPassphrase( 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..c1dcd2d9 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/PasswordDialog.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/PasswordDialog.kt @@ -10,24 +10,21 @@ import android.content.DialogInterface import android.os.Bundle import android.view.KeyEvent import android.view.WindowManager +import androidx.core.os.bundleOf import androidx.core.widget.doOnTextChanged import androidx.fragment.app.DialogFragment +import androidx.fragment.app.setFragmentResult import app.passwordstore.R import app.passwordstore.databinding.DialogPasswordEntryBinding import app.passwordstore.util.extensions.finish import app.passwordstore.util.extensions.unsafeLazy 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 binding by unsafeLazy { DialogPasswordEntryBinding.inflate(layoutInflater) } private var isError: Boolean = false - private val _password = MutableStateFlow(null) - val password = _password.asStateFlow() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = MaterialAlertDialogBuilder(requireContext()) @@ -66,7 +63,12 @@ class PasswordDialog : DialogFragment() { } private fun setPasswordAndDismiss() { - _password.update { binding.passwordEditText.text.toString() } + val password = binding.passwordEditText.text.toString() + setFragmentResult(PASSWORD_RESULT_KEY, bundleOf(PASSWORD_RESULT_KEY to password)) dismissAllowingStateLoss() } + + companion object { + const val PASSWORD_RESULT_KEY = "password_result" + } }