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 ++++++++++++++++------ app/src/main/res/values/strings.xml | 1 + 2 files changed, 67 insertions(+), 26 deletions(-) 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() + } + } + } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index adac6d51..b9b90294 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,6 +372,7 @@ Place shortcut on home screen Create new password or folder Failed to import PGP key + An existing key with this ID was found, do you want to replace it? Successfully imported PGP key The key ID of the imported key is given below, please review it for correctness:\n%1$s PGP settings -- cgit v1.2.3