aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt
diff options
context:
space:
mode:
authorHarsh Shandilya <msfjarvis@gmail.com>2020-06-12 14:58:15 +0000
committerGitHub <noreply@github.com>2020-06-12 20:28:15 +0530
commitd8231e112afb501c43041a6f839ab8285f400f77 (patch)
tree6785a8d0c6a70a3017ce5d62fe6b8234d5564f07 /app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt
parentbf33fb2c88a208931340201e95a524b274d70b31 (diff)
downloadHardenedPass-d8231e112afb501c43041a6f839ab8285f400f77.tar.gz
HardenedPass-d8231e112afb501c43041a6f839ab8285f400f77.zip
Break down PGP Activity into focused sections (#776)
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt202
1 files changed, 202 insertions, 0 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt
new file mode 100644
index 00000000..b7d7adcd
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package com.zeapo.pwdstore.crypto
+
+import android.content.Intent
+import android.graphics.Typeface
+import android.os.Bundle
+import android.text.method.PasswordTransformationMethod
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import androidx.activity.result.IntentSenderRequest
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult
+import androidx.lifecycle.lifecycleScope
+import com.github.ajalt.timberkt.e
+import com.zeapo.pwdstore.PasswordEntry
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.databinding.DecryptLayoutBinding
+import com.zeapo.pwdstore.utils.viewBinding
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import me.msfjarvis.openpgpktx.util.OpenPgpApi
+import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
+import org.openintents.openpgp.IOpenPgpService2
+import java.io.ByteArrayOutputStream
+import java.io.File
+
+class DecryptActivity : BasePgpActivity(), OpenPgpServiceConnection.OnBound {
+ private val binding by viewBinding(DecryptLayoutBinding::inflate)
+
+ private val relativeParentPath by lazy { getParentPath(fullPath, repoPath) }
+ private var passwordEntry: PasswordEntry? = null
+
+ private val userInteractionRequiredResult = registerForActivityResult(StartIntentSenderForResult()) { result ->
+ if (result.data == null) {
+ setResult(RESULT_CANCELED, null)
+ finish()
+ return@registerForActivityResult
+ }
+
+ when (result.resultCode) {
+ RESULT_OK -> decryptAndVerify(result.data)
+ RESULT_CANCELED -> {
+ setResult(RESULT_CANCELED, result.data)
+ finish()
+ }
+ }
+ }
+
+ private val openKeychainResult = registerForActivityResult(StartActivityForResult()) {
+ decryptAndVerify()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ bindToOpenKeychain(this, openKeychainResult)
+ title = name
+ with(binding) {
+ setContentView(root)
+ passwordCategory.text = relativeParentPath
+ passwordFile.text = name
+ passwordFile.setOnLongClickListener {
+ copyTextToClipboard(name)
+ true
+ }
+ try {
+ passwordLastChanged.text = resources.getString(R.string.last_changed, lastChangedString)
+ } catch (e: RuntimeException) {
+ passwordLastChanged.visibility = View.GONE
+ }
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.pgp_handler, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ android.R.id.home -> onBackPressed()
+ R.id.edit_password -> editPassword()
+ R.id.share_password_as_plaintext -> shareAsPlaintext()
+ R.id.copy_password -> copyPasswordToClipboard(passwordEntry?.password)
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun onBound(service: IOpenPgpService2) {
+ super.onBound(service)
+ decryptAndVerify()
+ }
+
+ override fun onError(e: Exception) {
+ e(e)
+ }
+
+ /**
+ * Edit the current password and hide all the fields populated by encrypted data so that when
+ * the result triggers they can be repopulated with new data.
+ */
+ private fun editPassword() {
+ val intent = Intent(this, PasswordCreationActivity::class.java)
+ intent.putExtra("FILE_PATH", relativeParentPath)
+ intent.putExtra("REPO_PATH", repoPath)
+ intent.putExtra(PasswordCreationActivity.EXTRA_FILE_NAME, name)
+ intent.putExtra(PasswordCreationActivity.EXTRA_PASSWORD, passwordEntry?.password)
+ intent.putExtra(PasswordCreationActivity.EXTRA_EXTRA_CONTENT, passwordEntry?.extraContent)
+ intent.putExtra(PasswordCreationActivity.EXTRA_EDITING, true)
+ startActivity(intent)
+ finish()
+ }
+
+ private fun shareAsPlaintext() {
+ val sendIntent = Intent().apply {
+ action = Intent.ACTION_SEND
+ putExtra(Intent.EXTRA_TEXT, passwordEntry?.password)
+ type = "text/plain"
+ }
+ // Always show a picker to give the user a chance to cancel
+ startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_plaintext_password_to)))
+ }
+
+ private fun decryptAndVerify(receivedIntent: Intent? = null) {
+ if (api == null) {
+ bindToOpenKeychain(this, openKeychainResult)
+ return
+ }
+ val data = receivedIntent ?: Intent()
+ data.action = OpenPgpApi.ACTION_DECRYPT_VERIFY
+
+ val inputStream = File(fullPath).inputStream()
+ val outputStream = ByteArrayOutputStream()
+
+ lifecycleScope.launch(Dispatchers.IO) {
+ api?.executeApiAsync(data, inputStream, outputStream) { result ->
+ when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
+ OpenPgpApi.RESULT_CODE_SUCCESS -> {
+ try {
+ val showPassword = settings.getBoolean("show_password", true)
+ val showExtraContent = settings.getBoolean("show_extra_content", true)
+ val monoTypeface = Typeface.createFromAsset(assets, "fonts/sourcecodepro.ttf")
+ val entry = PasswordEntry(outputStream)
+
+ passwordEntry = entry
+
+ with(binding) {
+ if (entry.password.isEmpty()) {
+ passwordTextContainer.visibility = View.GONE
+ } else {
+ passwordTextContainer.visibility = View.VISIBLE
+ passwordText.typeface = monoTypeface
+ passwordText.setText(entry.password)
+ if (!showPassword) {
+ passwordText.transformationMethod = PasswordTransformationMethod.getInstance()
+ }
+ passwordTextContainer.setOnClickListener { copyPasswordToClipboard(entry.password) }
+ passwordText.setOnClickListener { copyPasswordToClipboard(entry.password) }
+ }
+
+ if (entry.hasExtraContent()) {
+ extraContentContainer.visibility = View.VISIBLE
+ extraContent.typeface = monoTypeface
+ extraContent.setText(entry.extraContentWithoutUsername)
+ if (!showExtraContent) {
+ extraContent.transformationMethod = PasswordTransformationMethod.getInstance()
+ }
+ extraContentContainer.setOnClickListener { copyTextToClipboard(entry.extraContentWithoutUsername) }
+ extraContent.setOnClickListener { copyTextToClipboard(entry.extraContentWithoutUsername) }
+
+ if (entry.hasUsername()) {
+ usernameText.typeface = monoTypeface
+ usernameText.setText(entry.username)
+ usernameTextContainer.setEndIconOnClickListener { copyTextToClipboard(entry.username) }
+ usernameTextContainer.visibility = View.VISIBLE
+ } else {
+ usernameTextContainer.visibility = View.GONE
+ }
+ }
+ }
+
+ if (settings.getBoolean("copy_on_decrypt", true)) {
+ copyPasswordToClipboard(entry.password)
+ }
+ } catch (e: Exception) {
+ e(e)
+ }
+ }
+ OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> {
+ val sender = getUserInteractionRequestIntent(result)
+ userInteractionRequiredResult.launch(IntentSenderRequest.Builder(sender).build())
+ }
+ OpenPgpApi.RESULT_CODE_ERROR -> handleError(result)
+ }
+ }
+ }
+ }
+}