From 4ed98c9fda6e3121e5ba9d9d4b001e9512240fd0 Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Sun, 17 Jul 2022 22:21:55 +0530 Subject: Refactor key import flow and implement support for replacing --- .../passwordstore/ui/pgp/PGPKeyImportActivity.kt | 92 ++++++++++++++++------ 1 file changed, 66 insertions(+), 26 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt b/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt index cd4ab07f..4ea1d0b5 100644 --- a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt @@ -13,7 +13,10 @@ import app.passwordstore.R import app.passwordstore.crypto.KeyUtils.tryGetId import app.passwordstore.crypto.PGPKey import app.passwordstore.crypto.PGPKeyManager -import com.github.michaelbull.result.mapBoth +import app.passwordstore.crypto.errors.KeyAlreadyExistsException +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result import com.github.michaelbull.result.runCatching import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint @@ -23,6 +26,11 @@ import kotlinx.coroutines.runBlocking @AndroidEntryPoint class PGPKeyImportActivity : AppCompatActivity() { + /** + * A [ByteArray] containing the contents of the previously selected file. This is necessary for + * the replacement case where we do not want users to have to pick the file again. + */ + private var lastBytes: ByteArray? = null @Inject lateinit var keyManager: PGPKeyManager private val pgpKeyImportAction = @@ -35,36 +43,68 @@ class PGPKeyImportActivity : AppCompatActivity() { contentResolver.openInputStream(uri) ?: throw IllegalStateException("Failed to open selected file") val bytes = keyInputStream.use { `is` -> `is`.readBytes() } - val (key, error) = runBlocking { keyManager.addKey(PGPKey(bytes)) } - if (error != null) throw error - key + importKey(bytes, false) } - .mapBoth( - { key -> - if (key != null) { - MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.pgp_key_import_succeeded)) - .setMessage(getString(R.string.pgp_key_import_succeeded_message, tryGetId(key))) - .setPositiveButton(android.R.string.ok) { _, _ -> finish() } - .setOnCancelListener { finish() } - .show() - } else { - finish() - } - }, - { throwable -> - MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.pgp_key_import_failed)) - .setMessage(throwable.message) - .setPositiveButton(android.R.string.ok) { _, _ -> finish() } - .setOnCancelListener { finish() } - .show() - } - ) + .run(::handleImportResult) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pgpKeyImportAction.launch(arrayOf("*/*")) } + + override fun onDestroy() { + lastBytes = null + super.onDestroy() + } + + private fun importKey(bytes: ByteArray, replace: Boolean): PGPKey? { + lastBytes = bytes + val (key, error) = runBlocking { keyManager.addKey(PGPKey(bytes), replace = replace) } + if (replace) { + lastBytes = null + } + if (error != null) throw error + return key + } + + private fun handleImportResult(result: Result) { + when (result) { + is Ok -> { + val key = result.value + if (key == null) { + finish() + // This return convinces Kotlin that the control flow for `key == null` definitely + // terminates here and allows for a smart cast below. + return + } + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.pgp_key_import_succeeded)) + .setMessage(getString(R.string.pgp_key_import_succeeded_message, tryGetId(key))) + .setPositiveButton(android.R.string.ok) { _, _ -> finish() } + .setOnCancelListener { finish() } + .show() + } + is Err -> { + if (result.error is KeyAlreadyExistsException && lastBytes != null) { + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.pgp_key_import_failed)) + .setMessage(getString(R.string.pgp_key_import_failed_replace_message)) + .setPositiveButton(R.string.dialog_yes) { _, _ -> + handleImportResult(runCatching { importKey(lastBytes!!, replace = true) }) + } + .setNegativeButton(R.string.dialog_no) { _, _ -> finish() } + .setOnCancelListener { finish() } + .show() + } else { + MaterialAlertDialogBuilder(this) + .setTitle(getString(R.string.pgp_key_import_failed)) + .setMessage(result.error.message) + .setPositiveButton(android.R.string.ok) { _, _ -> finish() } + .setOnCancelListener { finish() } + .show() + } + } + } + } } -- cgit v1.2.3