From 6ea41cf334bc41f2446889161cc64b8d7a455798 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 6 Mar 2025 10:15:34 +0200 Subject: [PATCH 01/13] Updated some dependencies + refactored code --- FlowCrypt/build.gradle.kts | 14 +++++++------- build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/FlowCrypt/build.gradle.kts b/FlowCrypt/build.gradle.kts index 4c77e3d123..f8a2bcd673 100644 --- a/FlowCrypt/build.gradle.kts +++ b/FlowCrypt/build.gradle.kts @@ -34,6 +34,13 @@ android { namespace = "com.flowcrypt.email" defaultConfig { + /* + The following argument makes the Android Test Orchestrator run its + "pm clear" command after each test invocation. This command ensures + that the app"s state is completely cleared between tests. + */ + testInstrumentationRunnerArguments += mapOf("clearPackageData" to "true") + applicationId = "com.flowcrypt.email" minSdk = extra["minSdkVersion"] as Int targetSdk = extra["targetSdkVersion"] as Int @@ -41,13 +48,6 @@ android { versionName = extra["appVersionName"] as String testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" buildConfigField("int", "MIN_SDK_VERSION", "$minSdk") - - /* - The following argument makes the Android Test Orchestrator run its - "pm clear" command after each test invocation. This command ensures - that the app"s state is completely cleared between tests. - */ - testInstrumentationRunnerArguments += mapOf("clearPackageData" to "true") multiDexEnabled = true } diff --git a/build.gradle.kts b/build.gradle.kts index ddac053a26..9c4351b58f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id("com.android.application") version "8.7.3" apply false + id("com.android.application") version "8.9.0" apply false id("org.jetbrains.kotlin.android") version "2.1.10" apply false id("androidx.navigation.safeargs.kotlin") version "2.8.8" apply false id("com.starter.easylauncher") version "6.4.0" apply false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df91a54a0a..f1b9d03013 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -6,6 +6,6 @@ #Thu Dec 19 13:22:07 EET 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 3d904b81855647fabe3ca9be3f1a94f406806b57 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 6 Mar 2025 11:14:42 +0200 Subject: [PATCH 02/13] Added AlternativeContentMsgBlock. Fixed 'reply' issue.| #2972 --- .../model/AlternativeContentMsgBlock.kt | 29 +++++++++++++++ .../api/retrofit/response/model/MsgBlock.kt | 10 ++++-- .../flowcrypt/email/security/pgp/PgpMsg.kt | 36 ++++++++++++++++--- 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt new file mode 100644 index 0000000000..5edc3747ea --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt @@ -0,0 +1,29 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.api.retrofit.response.model + +import com.google.gson.annotations.Expose +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize + +/** + * @author Denys Bondarenko + */ +@Parcelize +data class AlternativeContentMsgBlock( + @Expose override val error: MsgBlockError? = null, + @Expose val htmlVersionBlock: MsgBlock, + @Expose val plainVersionBlock: MsgBlock, + @Expose override val isOpenPGPMimeSigned: Boolean +) : MsgBlock { + @IgnoredOnParcel + @Expose + override val content: String? = null + + @IgnoredOnParcel + @Expose + override val type: MsgBlock.Type = MsgBlock.Type.ALTERNATIVE +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/MsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/MsgBlock.kt index 8d54b7db0f..3a7438f530 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/MsgBlock.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/MsgBlock.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.api.retrofit.response.model @@ -83,7 +83,10 @@ interface MsgBlock : Parcelable { ENCRYPTED_SUBJECT, @SerializedName("securityWarning") - SECURITY_WARNING; + SECURITY_WARNING, + + @SerializedName("alternative") + ALTERNATIVE; fun isContentBlockType(): Boolean = CONTENT_BLOCK_TYPES.contains(this) @@ -105,7 +108,8 @@ interface MsgBlock : Parcelable { DECRYPTED_HTML, SIGNED_CONTENT, VERIFIED_MSG, - DECRYPTED_AND_OR_SIGNED_CONTENT + DECRYPTED_AND_OR_SIGNED_CONTENT, + ALTERNATIVE ) val DECRYPTED_CONTENT_BLOCK_TYPES = setOf( diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt index 1fc789b32f..48ea43b0c0 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt @@ -9,6 +9,7 @@ import android.content.Context import android.util.Base64 import androidx.core.util.PatternsCompat import com.flowcrypt.email.api.email.JavaEmailConstants +import com.flowcrypt.email.api.retrofit.response.model.AlternativeContentMsgBlock import com.flowcrypt.email.api.retrofit.response.model.AttMeta import com.flowcrypt.email.api.retrofit.response.model.AttMsgBlock import com.flowcrypt.email.api.retrofit.response.model.DecryptErrorMsgBlock @@ -1193,7 +1194,7 @@ object PgpMsg { val msgContentAsText = StringBuilder() for (block in allContentBlocks.filterNot { MimeUtils.isPlainImgAtt(it) }) { if (block.content != null) { - val content = block.content!! + val content = requireNotNull(block.content) when (block.type) { MsgBlock.Type.DECRYPTED_TEXT -> { val html = fmtMsgContentBlockAsHtml(content.toEscapedHtml(), FrameColor.GREEN) @@ -1248,6 +1249,20 @@ object PgpMsg { msgContentAsText.append(content).append('\n') } } + } else { + when (block) { + is AlternativeContentMsgBlock -> { + val htmlVersionBlock = block.htmlVersionBlock + htmlVersionBlock.content?.stripHtmlRootTags()?.let { html -> + msgContentAsHtml.append(fmtMsgContentBlockAsHtml(html, FrameColor.GREEN)) + } + + val plainVersionBlock = block.plainVersionBlock + msgContentAsText + .append(plainVersionBlock.content) + .append('\n') + } + } } } @@ -1502,15 +1517,26 @@ object PgpMsg { private fun fmtDecryptedAsSanitizedHtmlBlocks(decryptedContent: ByteArray?): Collection { if (decryptedContent == null) return emptyList() + val decryptedText = String(decryptedContent) val blocks = mutableListOf() - val strippedContent = stripFcReplyToken(extractFcAttachments(String(decryptedContent), blocks)) + val strippedContent = stripFcReplyToken(extractFcAttachments(decryptedText, blocks)) val armoredKeys = extractPublicKeysIfFound(strippedContent) val content = checkAndReturnQuotesFormatIfFound(strippedContent) ?: strippedContent.toEscapedHtml() blocks.add( - MsgBlockFactory.fromContent( - MsgBlock.Type.DECRYPTED_HTML, - content, + //we need to add two alternative versions: + //formatted HTML + original text(will be used for a reply) + AlternativeContentMsgBlock( + htmlVersionBlock = MsgBlockFactory.fromContent( + MsgBlock.Type.DECRYPTED_HTML, + content, + isOpenPGPMimeSigned = false + ), + plainVersionBlock = MsgBlockFactory.fromContent( + MsgBlock.Type.DECRYPTED_TEXT, + decryptedText, + isOpenPGPMimeSigned = false + ), isOpenPGPMimeSigned = false ) ) From 0f6c629c10cf3a209ba66484c513e8a5fc53c586 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 6 Mar 2025 11:15:13 +0200 Subject: [PATCH 03/13] Updated ParcelableTest.| #2972 --- .../com/flowcrypt/email/ParcelableTest.kt | 112 +++++++++++------- 1 file changed, 68 insertions(+), 44 deletions(-) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt index 41d5355b77..3da2f17a5a 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt @@ -9,10 +9,12 @@ import android.os.Parcel import android.os.Parcelable import com.flextrade.jfixture.JFixture import com.flowcrypt.email.api.email.model.OutgoingMessageInfo +import com.flowcrypt.email.api.retrofit.response.model.AlternativeContentMsgBlock import com.flowcrypt.email.api.retrofit.response.model.ClientConfiguration import com.flowcrypt.email.api.retrofit.response.model.DecryptedAndOrSignedContentMsgBlock import com.flowcrypt.email.api.retrofit.response.model.GenericMsgBlock import com.flowcrypt.email.api.retrofit.response.model.MsgBlock +import com.flowcrypt.email.api.retrofit.response.model.MsgBlockFactory import com.flowcrypt.email.jfixture.MsgBlockGenerationCustomization import com.flowcrypt.email.jfixture.SelectConstructorCustomisation import com.flowcrypt.email.model.MessageEncryptionType @@ -42,52 +44,74 @@ class ParcelableTest(val name: String, private val currentClass: Class Date: Fri, 7 Mar 2025 11:34:37 +0200 Subject: [PATCH 04/13] wip --- .../model/AlternativeContentMsgBlock.kt | 2 +- .../flowcrypt/email/security/pgp/PgpMsg.kt | 236 +++++++++++------- .../com/flowcrypt/email/ParcelableTest.kt | 4 +- 3 files changed, 151 insertions(+), 91 deletions(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt index 5edc3747ea..7c0aa36701 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt @@ -15,8 +15,8 @@ import kotlinx.parcelize.Parcelize @Parcelize data class AlternativeContentMsgBlock( @Expose override val error: MsgBlockError? = null, - @Expose val htmlVersionBlock: MsgBlock, @Expose val plainVersionBlock: MsgBlock, + @Expose val otherBlocks: List, @Expose override val isOpenPGPMimeSigned: Boolean ) : MsgBlock { @IgnoredOnParcel diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt index 48ea43b0c0..a6700c08e9 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt @@ -30,7 +30,6 @@ import com.flowcrypt.email.api.retrofit.response.model.VerificationResult import com.flowcrypt.email.core.msg.MimeUtils import com.flowcrypt.email.core.msg.RawBlockParser import com.flowcrypt.email.database.FlowCryptRoomDatabase -import com.flowcrypt.email.extensions.jakarta.mail.hasPgpThings import com.flowcrypt.email.extensions.jakarta.mail.isMultipart import com.flowcrypt.email.extensions.jakarta.mail.isMultipartAlternative import com.flowcrypt.email.extensions.jakarta.mail.isOpenPGPMimeEncrypted @@ -355,34 +354,56 @@ object PgpMsg { //it's a multipart that should be investigated. part.isMultipart() -> { val multiPart = part.content as Multipart - val isMultipartAlternative = part.isMultipartAlternative() - var isAlternativePartUsed = false - for (partCount in 0 until multiPart.count) { - val subPart = multiPart.getBodyPart(partCount) - if (!subPart.isMultipart() && isMultipartAlternative && multiPart.count > 1) { - //if it multipart/alternative case with more than 1 part - //we should handle only one part and skip other - if (isAlternativePartUsed) { - continue - } else if (subPart.isPlainText()) { - if (subPart.hasPgpThings()) { - isAlternativePartUsed = true - } else { - //we prefer to use HTML part if there are no PGP things - continue - } - } + if (part.isMultipartAlternative()) { + val parts = mutableListOf() + for (partCount in 0 until multiPart.count) { + parts.add(multiPart.getBodyPart(partCount)) } - blocks.addAll( - extractMsgBlocksFromPart( - part = subPart, + val partWithPlainText = parts.firstOrNull { it.isPlainText() } + if (partWithPlainText != null) { + val plainVersionBlock = extractMsgBlocksFromPart( + part = partWithPlainText, verificationPublicKeys = verificationPublicKeys, secretKeys = secretKeys, protector = protector, isOpenPGPMimeSigned = isOpenPGPMimeSigned + ).firstOrNull() + + val otherBlocks = (parts - partWithPlainText).flatMap { alternativePart -> + extractMsgBlocksFromPart( + part = alternativePart, + verificationPublicKeys = verificationPublicKeys, + secretKeys = secretKeys, + protector = protector, + isOpenPGPMimeSigned = isOpenPGPMimeSigned + ) + } + + if (plainVersionBlock != null) { + blocks.add( + AlternativeContentMsgBlock( + plainVersionBlock = plainVersionBlock, + otherBlocks = otherBlocks, + isOpenPGPMimeSigned = isOpenPGPMimeSigned + ) + ) + } else { + blocks.addAll(otherBlocks) + } + } + } else { + for (partCount in 0 until multiPart.count) { + blocks.addAll( + extractMsgBlocksFromPart( + part = multiPart.getBodyPart(partCount), + verificationPublicKeys = verificationPublicKeys, + secretKeys = secretKeys, + protector = protector, + isOpenPGPMimeSigned = isOpenPGPMimeSigned + ) ) - ) + } } } @@ -839,7 +860,7 @@ object PgpMsg { } if (!isEncrypted) { - isEncrypted = messageMetadata?.isEncrypted ?: false + isEncrypted = messageMetadata?.isEncrypted == true } if (messageMetadata?.isSigned == true) { @@ -1192,76 +1213,42 @@ object PgpMsg { val msgContentAsHtml = StringBuilder() val msgContentAsText = StringBuilder() - for (block in allContentBlocks.filterNot { MimeUtils.isPlainImgAtt(it) }) { - if (block.content != null) { - val content = requireNotNull(block.content) - when (block.type) { - MsgBlock.Type.DECRYPTED_TEXT -> { - val html = fmtMsgContentBlockAsHtml(content.toEscapedHtml(), FrameColor.GREEN) - msgContentAsHtml.append(html) - msgContentAsText.append(content).append('\n') - } - - MsgBlock.Type.DECRYPTED_HTML -> { - // Typescript comment: todo: add support for inline imgs? when included using cid - var html = content.stripHtmlRootTags() - html = fmtMsgContentBlockAsHtml(html, FrameColor.GREEN) - msgContentAsHtml.append(html) - msgContentAsText - .append(sanitizeHtmlStripAllTags(content)?.unescapeHtml()) - .append('\n') - } - MsgBlock.Type.PLAIN_TEXT -> { - val html = fmtMsgContentBlockAsHtml( - checkAndReturnQuotesFormatIfFound(content) ?: content.toEscapedHtml(), - if (block.isOpenPGPMimeSigned) FrameColor.GRAY else FrameColor.PLAIN - ) - msgContentAsHtml.append(html) - msgContentAsText.append(content).append('\n') - } - - MsgBlock.Type.PLAIN_HTML -> { - val stripped = content.stripHtmlRootTags() - val dirtyHtmlWithImgs = fillInlineHtmlImages(stripped, inlineImagesByCid) - msgContentAsHtml.append( - fmtMsgContentBlockAsHtml( - dirtyHtmlWithImgs, - if (block.isOpenPGPMimeSigned) FrameColor.GRAY else FrameColor.PLAIN - ) - ) - val text = sanitizeHtmlStripAllTags(dirtyHtmlWithImgs)?.unescapeHtml() - msgContentAsText.append(text).append('\n') - } - - MsgBlock.Type.VERIFIED_MSG, MsgBlock.Type.SIGNED_CONTENT -> { - msgContentAsHtml.append(fmtMsgContentBlockAsHtml(content, FrameColor.GRAY)) - msgContentAsText.append(sanitizeHtmlStripAllTags(content)).append('\n') - } + fun collectDataFromMsgBlock( + block: MsgBlock, + useHtml: Boolean = true, + usePlainText: Boolean = true + ) = { + handleMsgBlock(block, inlineImagesByCid) { html, plainText -> + html?.takeIf { useHtml }?.let { + msgContentAsHtml.append(html) + } + plainText?.takeIf { usePlainText }?.let { text -> + msgContentAsText.append(text).append('\n') + } + } + } - else -> { - msgContentAsHtml.append( - fmtMsgContentBlockAsHtml( - content, - if (block.isOpenPGPMimeSigned) FrameColor.GRAY else FrameColor.PLAIN - ) - ) - msgContentAsText.append(content).append('\n') + for (block in allContentBlocks.filterNot { MimeUtils.isPlainImgAtt(it) }) { + when (block) { + is AlternativeContentMsgBlock -> { + val htmlVersionBlock = block.otherBlocks.firstOrNull() + htmlVersionBlock?.let { htmlBlock -> + collectDataFromMsgBlock( + block = htmlBlock, + useHtml = true, + usePlainText = false + ).invoke() } + collectDataFromMsgBlock( + block = block.plainVersionBlock, + useHtml = false, + usePlainText = true + ).invoke() } - } else { - when (block) { - is AlternativeContentMsgBlock -> { - val htmlVersionBlock = block.htmlVersionBlock - htmlVersionBlock.content?.stripHtmlRootTags()?.let { html -> - msgContentAsHtml.append(fmtMsgContentBlockAsHtml(html, FrameColor.GREEN)) - } - val plainVersionBlock = block.plainVersionBlock - msgContentAsText - .append(plainVersionBlock.content) - .append('\n') - } + else -> { + collectDataFromMsgBlock(block = block).invoke() } } } @@ -1318,6 +1305,75 @@ object PgpMsg { ) } + private fun handleMsgBlock( + block: MsgBlock, + inlineImagesByCid: MutableMap, + action: (html: String?, plainText: String?) -> Unit + ) { + val content = block.content ?: return + when (block.type) { + MsgBlock.Type.DECRYPTED_TEXT -> { + action.invoke( + fmtMsgContentBlockAsHtml(content.toEscapedHtml(), FrameColor.GREEN), + content + ) + } + + MsgBlock.Type.DECRYPTED_HTML -> { + // Typescript comment: todo: add support for inline imgs? when included using cid + action.invoke( + fmtMsgContentBlockAsHtml(content.stripHtmlRootTags(), FrameColor.GREEN), + "${sanitizeHtmlStripAllTags(content)?.unescapeHtml()}" + ) + } + + MsgBlock.Type.PLAIN_TEXT -> { + action.invoke( + fmtMsgContentBlockAsHtml( + checkAndReturnQuotesFormatIfFound(content) ?: content.toEscapedHtml(), + if (block.isOpenPGPMimeSigned) FrameColor.GRAY else FrameColor.PLAIN + ), + content + ) + } + + MsgBlock.Type.PLAIN_HTML -> { + val stripped = content.stripHtmlRootTags() + val dirtyHtmlWithImages = fillInlineHtmlImages(stripped, inlineImagesByCid) + val html = fmtMsgContentBlockAsHtml( + dirtyHtmlWithImages, + if (block.isOpenPGPMimeSigned) { + FrameColor.GRAY + } else { + FrameColor.PLAIN + } + ) + action.invoke(html, sanitizeHtmlStripAllTags(dirtyHtmlWithImages)?.unescapeHtml()) + } + + MsgBlock.Type.VERIFIED_MSG, MsgBlock.Type.SIGNED_CONTENT -> { + action.invoke( + fmtMsgContentBlockAsHtml(content, FrameColor.GRAY), + sanitizeHtmlStripAllTags(content) + ) + } + + else -> { + action.invoke( + fmtMsgContentBlockAsHtml( + content, + if (block.isOpenPGPMimeSigned) { + FrameColor.GRAY + } else { + FrameColor.PLAIN + } + ), + content + ) + } + } + } + private fun checkAndReturnQuotesFormatIfFound(content: String): String? { return buildQuotes(originalContent = content, unwrapContent = false)?.outerHtml() } @@ -1527,10 +1583,12 @@ object PgpMsg { //we need to add two alternative versions: //formatted HTML + original text(will be used for a reply) AlternativeContentMsgBlock( - htmlVersionBlock = MsgBlockFactory.fromContent( + otherBlocks = listOf( + MsgBlockFactory.fromContent( MsgBlock.Type.DECRYPTED_HTML, content, isOpenPGPMimeSigned = false + ) ), plainVersionBlock = MsgBlockFactory.fromContent( MsgBlock.Type.DECRYPTED_TEXT, diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt index 3da2f17a5a..15a6e0698f 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt @@ -98,10 +98,12 @@ class ParcelableTest(val name: String, private val currentClass: Class Date: Mon, 10 Mar 2025 08:13:52 +0200 Subject: [PATCH 05/13] wip --- .../flowcrypt/email/security/pgp/PgpMsg.kt | 2 +- .../email/security/pgp/PgpMsgTest.kt | 41 ++++++++++++++++++- ...alert-20210416-084836-UTC-text-content.txt | 20 +++++---- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt index a6700c08e9..31747cae2b 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt @@ -1374,7 +1374,7 @@ object PgpMsg { } } - private fun checkAndReturnQuotesFormatIfFound(content: String): String? { + fun checkAndReturnQuotesFormatIfFound(content: String): String? { return buildQuotes(originalContent = content, unwrapContent = false)?.outerHtml() } diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt index b10ca31641..41171816af 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt @@ -487,7 +487,7 @@ class PgpMsgTest { secretKeys = PGPSecretKeyRingCollection(keys.map { it.keyRing }), protector = SecretKeyRingProtector.unprotectedKeys() ) - assertEquals("Below\n\n[image: image.png]\nAbove", result.text) + assertEquals("Below\n[image: image.png]\nAbove", result.text) assertEquals(false, result.verificationResult.hasEncryptedParts) assertEquals(2, result.blocks.size) val block = result.blocks[0] @@ -521,7 +521,7 @@ class PgpMsgTest { val textContent = loadResourceAsString( "other/plain-google-security-alert-20210416-084836-UTC-text-content.txt" ) - assertEquals(textContent, result.text) + assertEquals(textContent.replace("\n", "\r\n"), result.text) assertEquals(false, result.verificationResult.hasEncryptedParts) assertEquals(1, result.blocks.size) val block = result.blocks[0] @@ -613,6 +613,17 @@ class PgpMsgTest { assertEquals(1, document.select("details").size) assertNotNull(document.select("summary").first()) assertEquals(1, document.select("summary").size) + + //check that plain text was generated correctly and quotes for reply will be correct + val documentForPlainVersion = Jsoup.parse( + requireNotNull(PgpMsg.checkAndReturnQuotesFormatIfFound(processedMimeMessageResult.text)), + "", + Parser.xmlParser() + ) + + val quotesForPlainVersion = documentForPlainVersion.select("blockquote") + assertEquals(1, quotesForPlainVersion.size) + assertTrue(quotesForPlainVersion[0].text().startsWith("Today, Android 15")) } @Test @@ -740,6 +751,19 @@ class PgpMsgTest { assertTrue(quotes[0].text().startsWith("2 Creating custom build configurations requires")) assertTrue(quotes[1].text().startsWith("reply 1")) assertTrue(quotes[2].text().startsWith("1 The Android build system")) + + //check that plain text was generated correctly and quotes for reply will be correct + val documentForPlainVersion = Jsoup.parse( + requireNotNull(PgpMsg.checkAndReturnQuotesFormatIfFound(processedMimeMessageResult.text)), + "", + Parser.xmlParser() + ) + + val quotesForPlainVersion = documentForPlainVersion.select("blockquote") + assertEquals(3, quotesForPlainVersion.size) + assertTrue(quotes[0].text().startsWith("2 Creating custom build configurations requires")) + assertTrue(quotes[1].text().startsWith("reply 1")) + assertTrue(quotes[2].text().startsWith("1 The Android build system")) } @Test @@ -824,6 +848,19 @@ class PgpMsgTest { assertTrue(quotes[0].text().startsWith("Sender 2")) assertTrue(quotes[1].text().startsWith("Reply 1")) assertTrue(quotes[2].text().startsWith("Sender 1")) + + //check that plain text was generated correctly and quotes for reply will be correct + val documentForPlainVersion = Jsoup.parse( + requireNotNull(PgpMsg.checkAndReturnQuotesFormatIfFound(processedMimeMessageResult.text)), + "", + Parser.xmlParser() + ) + + val quotesForPlainVersion = documentForPlainVersion.select("blockquote") + assertEquals(3, quotesForPlainVersion.size) + assertTrue(quotesForPlainVersion[0].text().startsWith("Sender 2")) + assertTrue(quotesForPlainVersion[1].text().startsWith("Reply 1")) + assertTrue(quotesForPlainVersion[2].text().startsWith("Sender 1")) } private data class RenderedBlock( diff --git a/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-text-content.txt b/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-text-content.txt index 4b22b87ac2..69effe5ae6 100644 --- a/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-text-content.txt +++ b/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-text-content.txt @@ -1,13 +1,15 @@ -[remote content blocked for your privacy] +[image: Google] +FlowCrypt iOS App was granted access to your Google Account -FlowCrypt iOS App was granted access to your Google Account -[img]flowcrypt.compatibility@gmail.com - -[img] -If you did not grant access, you should check this activity and secure your account. -[img] -[img]Check activity + +flowcrypt.compatibility@gmail.com + +If you did not grant access, you should check this activity and secure your +account. +Check activity + You can also see security activity at https://myaccount.google.com/notifications -You received this email to let you know about important changes to your Google Account and services. +You received this email to let you know about important changes to your +Google Account and services. © 2021 Google LLC, 1600 Amphitheatre Parkway, Mountain View, CA 94043, USA \ No newline at end of file From cec323c7c65b5938c27a36a2f518689cc5c6e5a8 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Mon, 10 Mar 2025 10:40:24 +0200 Subject: [PATCH 06/13] wip --- .../flowcrypt/email/security/pgp/ProcessMimeMessageTest.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/ProcessMimeMessageTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/ProcessMimeMessageTest.kt index af5e9ea439..d0c1ba00c3 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/ProcessMimeMessageTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/ProcessMimeMessageTest.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.security.pgp @@ -37,8 +37,9 @@ class ProcessMimeMessageTest { ) assertEquals( - "It's an encrypted message\n\n \n " + - "\n \n Sent with ProtonMail secure email.", + "It's an encrypted message\n" + + "\n" + + "Sent with ProtonMail secure email.", processedMimeMessageResult.text ) From ed65d3749174890e0ae1d8790bc07aaa8403a8e3 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Mon, 10 Mar 2025 12:17:25 +0200 Subject: [PATCH 07/13] Fixed lint warnings --- .../com/flowcrypt/email/FlowCryptApplication.kt | 8 ++++---- .../flowcrypt/email/api/oauth/OAuth2Helper.kt | 8 ++++---- .../email/database/entity/AttachmentEntity.kt | 4 ++-- .../androidx/fragment/app/FragmentExt.kt | 6 +++--- .../email/extensions/kotlin/StringExt.kt | 4 ++-- .../service/MessagesNotificationManager.kt | 3 ++- .../email/service/PassPhrasesInRAMService.kt | 5 +++-- .../flowcrypt/email/ui/activity/MainActivity.kt | 10 ++++++---- .../activity/fragment/LegalSettingsFragment.kt | 17 ++++++----------- .../activity/fragment/MessagesListFragment.kt | 9 +++++---- .../base/BasePassphraseStrengthFragment.kt | 17 +++++++++-------- .../dialog/ChoosePrivateKeyDialogFragment.kt | 7 ++++--- .../dialog/ChoosePublicKeyDialogFragment.kt | 3 ++- .../notifications/ErrorNotificationManager.kt | 8 ++++---- .../notifications/SystemNotificationManager.kt | 6 +++--- .../flowcrypt/email/ui/widget/EmailWebView.kt | 9 +++++---- .../email/ui/widget/NonLockingScrollView.kt | 6 +++--- .../com/flowcrypt/email/util/AvatarGenerator.kt | 3 ++- .../com/flowcrypt/email/util/GeneralUtil.kt | 14 ++++++-------- .../com/flowcrypt/email/util/RFC6068Parser.kt | 5 +++-- .../email/util/SharedPreferencesHelper.kt | 14 ++++++-------- .../java/com/flowcrypt/email/util/UIUtil.kt | 8 +++++--- .../gson/UriJsonSerializerDeserializer.kt | 3 ++- 23 files changed, 91 insertions(+), 86 deletions(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt index 21499f5606..1841039f0c 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/FlowCryptApplication.kt @@ -8,6 +8,7 @@ package com.flowcrypt.email import android.app.Application import android.content.Context import android.util.Log +import androidx.core.content.edit import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner @@ -211,10 +212,9 @@ class FlowCryptApplication : Application(), Configuration.Provider { val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) if (sharedPreferences.all.isEmpty()) { if (!sharedPreferences.contains(Constants.PREF_KEY_INSTALL_VERSION)) { - sharedPreferences - .edit() - .putString(Constants.PREF_KEY_INSTALL_VERSION, BuildConfig.VERSION_NAME) - .apply() + sharedPreferences.edit { + putString(Constants.PREF_KEY_INSTALL_VERSION, BuildConfig.VERSION_NAME) + } } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/oauth/OAuth2Helper.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/oauth/OAuth2Helper.kt index f84e77990f..9a8361919a 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/oauth/OAuth2Helper.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/oauth/OAuth2Helper.kt @@ -1,11 +1,11 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.api.oauth -import android.net.Uri +import androidx.core.net.toUri import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationServiceConfiguration import net.openid.appauth.ResponseTypeValues @@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit * @author Denys Bondarenko */ class OAuth2Helper { - enum class Provider constructor(val openidConfigurationUrl: String) { + enum class Provider(val openidConfigurationUrl: String) { MICROSOFT(OPENID_CONFIGURATION_URL_MICROSOFT) } @@ -53,7 +53,7 @@ class OAuth2Helper { configuration, MICROSOFT_AZURE_APP_ID, ResponseTypeValues.CODE, - Uri.parse(redirectUri) + redirectUri.toUri() ) .setResponseMode(AuthorizationRequest.ResponseMode.QUERY) .setPrompt(AuthorizationRequest.Prompt.SELECT_ACCOUNT) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt index 2e750576ad..fc6e41bdbd 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt @@ -5,8 +5,8 @@ package com.flowcrypt.email.database.entity -import android.net.Uri import android.provider.BaseColumns +import androidx.core.net.toUri import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey @@ -68,7 +68,7 @@ data class AttachmentEntity( encodedSize = encodedSize ?: 0, type = type, id = attachmentId, - uri = if (fileUri.isNullOrEmpty()) null else Uri.parse(fileUri), + uri = if (fileUri.isNullOrEmpty()) null else fileUri.toUri(), fwdFolder = forwardedFolder, fwdUid = forwardedUid ?: -1, path = path, diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/androidx/fragment/app/FragmentExt.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/androidx/fragment/app/FragmentExt.kt index a64c3e7c9c..645fd489b4 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/androidx/fragment/app/FragmentExt.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/androidx/fragment/app/FragmentExt.kt @@ -17,7 +17,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.NavDirections -import androidx.navigation.Navigation +import androidx.navigation.findNavController import androidx.test.espresso.idling.CountingIdlingResource import com.flowcrypt.email.Constants import com.flowcrypt.email.R @@ -61,8 +61,8 @@ val Fragment.supportActionBar: ActionBar? val Fragment.navController: NavController? get() = activity?.let { try { - Navigation.findNavController(it, R.id.fragmentContainerView) - } catch (e: Exception) { + it.findNavController(R.id.fragmentContainerView) + } catch (_: Exception) { return@let null } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt index 4c9a937143..90a413da45 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt @@ -6,11 +6,11 @@ package com.flowcrypt.email.extensions.kotlin import android.content.Context -import android.graphics.Color import android.util.Patterns import android.util.TypedValue import android.webkit.MimeTypeMap import androidx.core.content.ContextCompat +import androidx.core.graphics.toColorInt import com.flowcrypt.email.R import com.flowcrypt.email.util.BetterInternetAddress import com.flowcrypt.email.util.UIUtil @@ -140,7 +140,7 @@ fun String?.parseAsColorBasedOnDefaultSettings( secondDefaultColorResourceId: Int = R.color.gray ): Int { return runCatching { - Color.parseColor(this) + requireNotNull(this).toColorInt() }.getOrElse { TypedValue().also { context.theme.resolveAttribute(defaultColorResourceId, it, true) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/service/MessagesNotificationManager.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/service/MessagesNotificationManager.kt index 84f4775ec3..1e18b2d9e5 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/service/MessagesNotificationManager.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/service/MessagesNotificationManager.kt @@ -14,6 +14,7 @@ import android.graphics.Bitmap import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.createBitmap import androidx.navigation.NavDeepLinkBuilder import androidx.preference.PreferenceManager import com.flowcrypt.email.BuildConfig @@ -264,7 +265,7 @@ class MessagesNotificationManager(context: Context) : CustomNotificationManager( val drawable = ResourcesCompat.getDrawable( context.resources, R.mipmap.ic_launcher, context.theme - ) ?: return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + ) ?: return createBitmap(1, 1) return GeneralUtil.drawableToBitmap(drawable) } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/service/PassPhrasesInRAMService.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/service/PassPhrasesInRAMService.kt index 5447e4db3c..1eb6b70a42 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/service/PassPhrasesInRAMService.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/service/PassPhrasesInRAMService.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.service @@ -197,7 +197,8 @@ class PassPhrasesInRAMService : BaseLifecycleService() { * @param context Interface to global information about an application environment. */ fun stop(context: Context) { - context.stopService(Intent(context, PassPhrasesInRAMService::class.java)) + val intent = Intent(context, PassPhrasesInRAMService::class.java) + context.stopService(intent) } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/MainActivity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/MainActivity.kt index 3305cfa927..7777c0d540 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/MainActivity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/MainActivity.kt @@ -31,6 +31,8 @@ import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.GravityCompat +import androidx.core.view.get +import androidx.core.view.size import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver @@ -349,7 +351,7 @@ class MainActivity : BaseActivity() { isClickable = false isFocusable = false isFocusableInTouchMode = false - isChecked = accountEntity.showOnlyEncrypted ?: false + isChecked = accountEntity.showOnlyEncrypted == true } if (!accountEntity.isGoogleSignInAccount) { @@ -372,7 +374,7 @@ class MainActivity : BaseActivity() { foldersManager?.run { val folders = getSortedServerFolders() + customLabels.sortedBy { it.folderAlias?.lowercase() } - val isGoogleAccount = activeAccount?.isGoogleSignInAccount ?: false + val isGoogleAccount = activeAccount?.isGoogleSignInAccount == true folders.forEach { localFolder -> val isGmailApiCategories = foldersManager.accountEntity.isGoogleSignInAccount @@ -407,9 +409,9 @@ class MainActivity : BaseActivity() { } private fun addOutboxLabel(foldersManager: FoldersManager, mailLabels: MenuItem?, label: String) { - val itemPosition = mailLabels?.subMenu?.size() ?: return + val itemPosition = mailLabels?.subMenu?.size ?: return if (itemPosition == 0) return - val menuItem = mailLabels.subMenu?.getItem(itemPosition - 1) ?: return + val menuItem = mailLabels.subMenu?.get(itemPosition - 1) ?: return val folder = foldersManager.getFolderByAlias(label) ?: return val view = layoutInflater.inflate( R.layout.navigation_view_item_with_amount, binding.navigationView, false diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/LegalSettingsFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/LegalSettingsFragment.kt index fc5bf7b2f2..f5fcac9ceb 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/LegalSettingsFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/LegalSettingsFragment.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.ui.activity.fragment @@ -14,6 +14,7 @@ import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient +import androidx.core.net.toUri import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter import com.flowcrypt.email.BuildConfig @@ -83,7 +84,7 @@ class LegalSettingsFragment : BaseFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) assetsPath = arguments?.getString(KEY_URL) - isRefreshEnabled = arguments?.getBoolean(KEY_IS_REFRESH_ENABLED, false) ?: false + isRefreshEnabled = arguments?.getBoolean(KEY_IS_REFRESH_ENABLED, false) == true } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -129,7 +130,7 @@ class LegalSettingsFragment : BaseFragment() { @Deprecated("Deprecated in Java", ReplaceWith("true")) override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { - showUrlUsingChromeCustomTabs(context = context, uri = Uri.parse(url)) + showUrlUsingChromeCustomTabs(context = context, uri = url.toUri()) return true } @@ -198,17 +199,11 @@ class LegalSettingsFragment : BaseFragment() { override fun createFragment(position: Int): Fragment { when (position) { TAB_POSITION_PRIVACY -> return WebViewFragment.newInstance( - Uri.parse( - Constants - .FLOWCRYPT_PRIVACY_URL - ), true + Constants.FLOWCRYPT_PRIVACY_URL.toUri(), true ) TAB_POSITION_TERMS -> return WebViewFragment.newInstance( - Uri.parse( - Constants - .FLOWCRYPT_TERMS_URL - ), true + Constants.FLOWCRYPT_TERMS_URL.toUri(), true ) TAB_POSITION_LICENCE -> return WebViewFragment.newInstance("html/license.htm") diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MessagesListFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MessagesListFragment.kt index db2ee2c368..5202cc3a2b 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MessagesListFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MessagesListFragment.kt @@ -30,6 +30,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toDrawable import androidx.core.view.MenuHost import androidx.core.view.MenuProvider import androidx.fragment.app.activityViewModels @@ -149,12 +150,12 @@ class MessagesListFragment : BaseFragment(), ListPr private val isOutboxFolder: Boolean get() { - return currentFolder?.isOutbox ?: false + return currentFolder?.isOutbox == true } private val isDraftsFolder: Boolean get() { - return currentFolder?.isDrafts ?: false + return currentFolder?.isDrafts == true } private val selectionObserver = object : SelectionTracker.SelectionObserver() { @@ -392,7 +393,7 @@ class MessagesListFragment : BaseFragment(), ListPr return } - val isOutbox = currentFolder?.isOutbox ?: false + val isOutbox = currentFolder?.isOutbox == true val isDraft = msgEntity.isDraft val isRawMsgAvailable = OutgoingMessagesManager.isMessageExist(requireContext(), msgEntity.id ?: -1) @@ -683,7 +684,7 @@ class MessagesListFragment : BaseFragment(), ListPr private val icon: Drawable? get() = context?.let { ContextCompat.getDrawable(it, R.drawable.ic_archive_white_24dp) } private val background: ColorDrawable? - get() = context?.let { ColorDrawable(ContextCompat.getColor(it, R.color.colorPrimaryDark)) } + get() = context?.let { ContextCompat.getColor(it, R.color.colorPrimaryDark).toDrawable() } override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BasePassphraseStrengthFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BasePassphraseStrengthFragment.kt index eaade17142..def11b0c8a 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BasePassphraseStrengthFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BasePassphraseStrengthFragment.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.ui.activity.fragment.base @@ -19,17 +19,18 @@ import android.widget.Toast import androidx.annotation.ColorRes import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat +import androidx.core.graphics.toColorInt import androidx.fragment.app.viewModels import androidx.viewbinding.ViewBinding import com.flowcrypt.email.Constants import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.extensions.androidx.fragment.app.countingIdlingResource -import com.flowcrypt.email.extensions.decrementSafely -import com.flowcrypt.email.extensions.incrementSafely import com.flowcrypt.email.extensions.androidx.fragment.app.launchAndRepeatWithViewLifecycle import com.flowcrypt.email.extensions.androidx.fragment.app.showInfoDialog import com.flowcrypt.email.extensions.androidx.fragment.app.toast +import com.flowcrypt.email.extensions.decrementSafely +import com.flowcrypt.email.extensions.incrementSafely import com.flowcrypt.email.jetpack.viewmodel.PasswordStrengthViewModel import com.flowcrypt.email.security.pgp.PgpPwd import com.flowcrypt.email.util.exception.IllegalTextForStrengthMeasuringException @@ -177,16 +178,16 @@ abstract class BasePassphraseStrengthFragment : BaseFragment private fun parseColor(): Int { return try { - Color.parseColor(pwdStrengthResult?.word?.color) - } catch (e: IllegalArgumentException) { + requireNotNull(pwdStrengthResult?.word?.color).toColorInt() + } catch (e: Exception) { e.printStackTrace() when (pwdStrengthResult?.word?.color) { - "orange" -> Color.parseColor("#FFA500") + "orange" -> "#FFA500".toColorInt() - "darkorange" -> Color.parseColor("#FF8C00") + "darkorange" -> "#FF8C00".toColorInt() - "darkred" -> Color.parseColor("#8B0000") + "darkred" -> "#8B0000".toColorInt() else -> Color.DKGRAY } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePrivateKeyDialogFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePrivateKeyDialogFragment.kt index d9996add9f..bac7df0f4c 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePrivateKeyDialogFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePrivateKeyDialogFragment.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.ui.activity.fragment.dialog @@ -12,15 +12,16 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf +import androidx.core.util.size import androidx.fragment.app.setFragmentResult import androidx.fragment.app.viewModels import androidx.navigation.fragment.navArgs import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.databinding.FragmentChoosePrivateKeyBinding -import com.flowcrypt.email.extensions.exceptionMsg import com.flowcrypt.email.extensions.androidx.fragment.app.navController import com.flowcrypt.email.extensions.androidx.fragment.app.toast +import com.flowcrypt.email.extensions.exceptionMsg import com.flowcrypt.email.jetpack.viewmodel.PrivateKeysViewModel import com.flowcrypt.email.security.model.PgpKeyRingDetails import com.flowcrypt.email.ui.activity.fragment.base.ListProgressBehaviour @@ -116,7 +117,7 @@ class ChoosePrivateKeyDialogFragment : BaseDialogFragment(), ListProgressBehavio val checkedItemPositions = binding?.listViewKeys?.checkedItemPositions val pgpKeyDetailsList = privateKeysViewModel.parseKeysResultLiveData.value?.data ?: emptyList() if (checkedItemPositions != null) { - for (i in 0 until checkedItemPositions.size()) { + for (i in 0 until checkedItemPositions.size) { val key = checkedItemPositions.keyAt(i) if (checkedItemPositions.get(key)) { selectedKeys.add(pgpKeyDetailsList[key]) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePublicKeyDialogFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePublicKeyDialogFragment.kt index 882e22167c..d65e794e3a 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePublicKeyDialogFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ChoosePublicKeyDialogFragment.kt @@ -16,6 +16,7 @@ import android.widget.ListView import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf +import androidx.core.util.size import androidx.fragment.app.setFragmentResult import androidx.fragment.app.viewModels import androidx.navigation.fragment.navArgs @@ -174,7 +175,7 @@ class ChoosePublicKeyDialogFragment : BaseDialogFragment(), View.OnClickListener val selectedAtts = ArrayList() val checkedItemPositions = listViewKeys?.checkedItemPositions if (checkedItemPositions != null) { - for (i in 0 until checkedItemPositions.size()) { + for (i in 0 until checkedItemPositions.size) { val key = checkedItemPositions.keyAt(i) if (checkedItemPositions.get(key)) { selectedAtts.add(atts[key]) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/ErrorNotificationManager.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/ErrorNotificationManager.kt index 3a2f4881f9..8e435a5299 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/ErrorNotificationManager.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/ErrorNotificationManager.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.ui.notifications @@ -10,9 +10,9 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import androidx.core.app.NotificationCompat +import androidx.core.net.toUri import androidx.navigation.NavDeepLinkBuilder import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.R @@ -147,7 +147,7 @@ class ErrorNotificationManager(context: Context) : CustomNotificationManager(con ): Intent { return when (account?.accountType) { AccountEntity.ACCOUNT_TYPE_GOOGLE, AccountEntity.ACCOUNT_TYPE_OUTLOOK -> { - val uri = Uri.parse("flowcrypt://email.flowcrypt.com/sign-in/recover_auth") + val uri = "flowcrypt://email.flowcrypt.com/sign-in/recover_auth".toUri() Intent(null, uri).apply { recoverableIntent?.let { val extras = Bundle().apply { @@ -158,7 +158,7 @@ class ErrorNotificationManager(context: Context) : CustomNotificationManager(con } } - else -> Intent(null, Uri.parse(context.getString(R.string.deep_link_server_settings))) + else -> Intent(null, context.getString(R.string.deep_link_server_settings).toUri()) } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/SystemNotificationManager.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/SystemNotificationManager.kt index 4b83ae1d54..d07f3debf6 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/SystemNotificationManager.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/notifications/SystemNotificationManager.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.ui.notifications @@ -10,8 +10,8 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.BitmapFactory -import android.net.Uri import androidx.core.app.NotificationCompat +import androidx.core.net.toUri import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.model.ClientConfiguration @@ -43,7 +43,7 @@ class SystemNotificationManager(context: Context) : CustomNotificationManager(co } else { Intent( Intent.ACTION_VIEW, - Uri.parse("flowcrypt://email.flowcrypt.com/settings/security") + "flowcrypt://email.flowcrypt.com/settings/security".toUri() ) } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/EmailWebView.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/EmailWebView.kt index 4c27d2db8a..4cf5e6307e 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/EmailWebView.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/EmailWebView.kt @@ -15,6 +15,7 @@ import android.webkit.WebChromeClient import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient +import androidx.core.net.toUri import com.flowcrypt.email.extensions.android.webkit.setupDayNight import com.flowcrypt.email.extensions.android.webkit.showUrlUsingChromeCustomTabs import com.flowcrypt.email.model.MessageType @@ -70,8 +71,8 @@ class EmailWebView : WebView { */ fun configure() { isVerticalScrollBarEnabled = false - scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY - overScrollMode = View.OVER_SCROLL_NEVER + scrollBarStyle = SCROLLBARS_INSIDE_OVERLAY + overScrollMode = OVER_SCROLL_NEVER webViewClient = CustomWebClient(context) webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView, newProgress: Int) { @@ -113,10 +114,10 @@ class EmailWebView : WebView { @Deprecated("Deprecated in Java") override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { return if (url.startsWith(SCHEME_MAILTO)) { - handleEmailLinks(Uri.parse(url)) + handleEmailLinks(url.toUri()) false } else { - showUrlUsingChromeCustomTabs(context = context, uri = Uri.parse(url)) + showUrlUsingChromeCustomTabs(context = context, uri = url.toUri()) true } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/NonLockingScrollView.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/NonLockingScrollView.kt index 5a853b5f1e..04e8765618 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/NonLockingScrollView.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/widget/NonLockingScrollView.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ /* @@ -30,8 +30,8 @@ import android.view.View import android.view.ViewGroup import android.webkit.WebView import android.widget.ScrollView +import androidx.core.view.isVisible import androidx.core.widget.NestedScrollView -import java.util.ArrayList /** * A [ScrollView] that will never lock scrolling in a particular direction. @@ -119,7 +119,7 @@ class NonLockingScrollView : NestedScrollView { } private fun canViewReceivePointerEvents(child: View): Boolean { - return child.visibility == View.VISIBLE || child.animation != null + return child.isVisible || child.animation != null } private fun getActionMasked(ev: MotionEvent): Int { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/AvatarGenerator.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/AvatarGenerator.kt index 4a77e7c2a6..8ed9b7b8f2 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/AvatarGenerator.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/AvatarGenerator.kt @@ -9,6 +9,7 @@ import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import androidx.core.graphics.createBitmap import kotlin.math.abs /** @@ -46,7 +47,7 @@ object AvatarGenerator { ) fun generate(text: String, bitmapWidth: Int, bitmapHeight: Int, fontSize: Float): Bitmap { - val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888) + val bitmap = createBitmap(bitmapWidth, bitmapHeight) val canvas = Canvas(bitmap) //draw a circle canvas.drawCircle( diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt index 14ac170e4f..d7cb7690bb 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt @@ -26,6 +26,8 @@ import android.webkit.MimeTypeMap import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat +import androidx.core.graphics.createBitmap +import androidx.core.net.toUri import androidx.preference.PreferenceManager import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.Constants @@ -369,7 +371,7 @@ class GeneralUtil { ) val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(url) + intent.data = url.toUri() if (intent.resolveActivity(context.packageManager) != null) { intent.data?.let { customTabsIntent.launchUrl(context, it) @@ -390,13 +392,9 @@ class GeneralUtil { } val bitmap: Bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) { - Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + createBitmap(1, 1) } else { - Bitmap.createBitmap( - drawable.intrinsicWidth, - drawable.intrinsicHeight, - Bitmap.Config.ARGB_8888 - ) + createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight) } val canvas = Canvas(bitmap) @@ -596,7 +594,7 @@ class GeneralUtil { messages: Array ) = withContext(Dispatchers.IO) { try { - val isSentFolder = imapFolder?.attributes?.contains("\\Sent") ?: true + val isSentFolder = imapFolder?.attributes?.contains("\\Sent") != false if (isSentFolder) { val emailAndNamePairs = mutableListOf>() diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/RFC6068Parser.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/RFC6068Parser.kt index e30c306da9..1f7a119a6c 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/RFC6068Parser.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/RFC6068Parser.kt @@ -1,12 +1,13 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.util import android.net.Uri import android.text.TextUtils +import androidx.core.net.toUri import com.flowcrypt.email.api.email.model.ExtraActionInfo import com.flowcrypt.email.api.email.model.InitializationData @@ -47,7 +48,7 @@ class RFC6068Parser { end = schemaSpecific.length } - val newUri = Uri.parse("foo://bar?" + uri.encodedQuery) + val newUri = "foo://bar?${uri.encodedQuery}".toUri() val params = CaseInsensitiveParamWrapper(newUri) // Extract the recipient's email address from the mailto URI if there's one. diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/SharedPreferencesHelper.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/SharedPreferencesHelper.kt index a7c643b66d..4b0e0154f4 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/SharedPreferencesHelper.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/SharedPreferencesHelper.kt @@ -1,13 +1,13 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.util +import android.annotation.SuppressLint import android.content.Context import android.content.SharedPreferences - import androidx.preference.PreferenceManager /** @@ -31,6 +31,7 @@ class SharedPreferencesHelper { return sharedPreferences.getBoolean(key, defaultValue) } + @SuppressLint("UseKtx") fun setBoolean(sharedPreferences: SharedPreferences, key: String, value: Boolean): Boolean { val editor = sharedPreferences.edit() editor.putBoolean(key, value) @@ -49,12 +50,7 @@ class SharedPreferencesHelper { return sharedPreferences.getLong(key, defaultValue) } - fun setLong(sharedPreferences: SharedPreferences, key: String, value: Long): Boolean { - val editor = sharedPreferences.edit() - editor.putLong(key, value) - return editor.commit() - } - + @SuppressLint("UseKtx") fun setString(sharedPreferences: SharedPreferences, key: String, value: String): Boolean { val editor = sharedPreferences.edit() editor.putString(key, value) @@ -65,6 +61,7 @@ class SharedPreferencesHelper { return sharedPreferences.getInt(key, defaultValue) } + @SuppressLint("UseKtx") fun setInt(sharedPreferences: SharedPreferences, key: String, value: Int): Boolean { val editor = sharedPreferences.edit() editor.putInt(key, value) @@ -77,6 +74,7 @@ class SharedPreferencesHelper { * @param context Interface to global information about an application environment. * @return Returns true if the new values were successfully written to persistent storage. */ + @SuppressLint("UseKtx") fun clear(context: Context): Boolean { return PreferenceManager.getDefaultSharedPreferences(context).edit().clear().commit() } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/UIUtil.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/UIUtil.kt index 38277918fb..6a9ec950f3 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/UIUtil.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/UIUtil.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.util @@ -15,6 +15,7 @@ import android.text.TextUtils import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.TextView +import androidx.core.graphics.createBitmap import java.io.ByteArrayOutputStream /** @@ -94,9 +95,10 @@ class UIUtil { val view = activity.window.decorView val width = view.width val height = view.height - val bitmap = Bitmap.createBitmap( + val bitmap = createBitmap( if (width > 0) width else 640, - if (height > 0) height else 480, Bitmap.Config.RGB_565 + if (height > 0) height else 480, + Bitmap.Config.RGB_565 ) val canvas = Canvas(bitmap) view.draw(canvas) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/gson/UriJsonSerializerDeserializer.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/gson/UriJsonSerializerDeserializer.kt index 74602486a7..3f0d1834aa 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/gson/UriJsonSerializerDeserializer.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/gson/UriJsonSerializerDeserializer.kt @@ -6,6 +6,7 @@ package com.flowcrypt.email.util.google.gson import android.net.Uri +import androidx.core.net.toUri import com.google.gson.JsonDeserializationContext import com.google.gson.JsonDeserializer import com.google.gson.JsonElement @@ -24,7 +25,7 @@ class UriJsonSerializerDeserializer : JsonDeserializer, JsonSerializer Date: Mon, 10 Mar 2025 12:23:47 +0200 Subject: [PATCH 08/13] wip --- .../com/flowcrypt/email/database/entity/AttachmentEntity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt index fc6e41bdbd..436c53be65 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/AttachmentEntity.kt @@ -68,7 +68,7 @@ data class AttachmentEntity( encodedSize = encodedSize ?: 0, type = type, id = attachmentId, - uri = if (fileUri.isNullOrEmpty()) null else fileUri.toUri(), + uri = fileUri?.takeIf { it.isNotEmpty() }?.toUri(), fwdFolder = forwardedFolder, fwdUid = forwardedUid ?: -1, path = path, From 643912c60fb9905e17675624ba01304ba41ab9c3 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Mon, 10 Mar 2025 18:13:29 +0200 Subject: [PATCH 09/13] wip --- .../flowcrypt/email/security/pgp/PgpMsg.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt index 31747cae2b..b04fad4a80 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt @@ -1200,7 +1200,10 @@ object PgpMsg { ): FormattedContentBlockResult { val inlineImagesByCid = mutableMapOf() val imagesAtTheBottom = mutableListOf() - for (plainImageBlock in allContentBlocks.filter { MimeUtils.isPlainImgAtt(it) }) { + val plainImageBlocks = filterBlocksViaTree(allContentBlocks) { + MimeUtils.isPlainImgAtt(it) + } + for (plainImageBlock in plainImageBlocks) { var contentId = (plainImageBlock as AttMsgBlock).attMeta.contentId ?: "" if (contentId.isNotEmpty()) { contentId = @@ -1305,6 +1308,25 @@ object PgpMsg { ) } + private fun filterBlocksViaTree( + blocks: List, + predicate: (MsgBlock) -> Boolean + ): List { + return mutableListOf().apply { + blocks.forEach { block -> + when { + block is AlternativeContentMsgBlock -> { + addAll(filterBlocksViaTree(block.otherBlocks, predicate)) + } + + predicate(block) -> { + add(block) + } + } + } + } + } + private fun handleMsgBlock( block: MsgBlock, inlineImagesByCid: MutableMap, From b43612d83e3f266fca6dca867d5f022bb1136655 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Tue, 11 Mar 2025 12:13:38 +0200 Subject: [PATCH 10/13] wip --- ...geWithOriginalAttachmentsComposeGmailApiFlow.kt | 2 +- ...geWithOriginalAttachmentsComposeGmailApiFlow.kt | 2 +- .../EncryptedReplyAllComposeGmailApiFlow.kt | 2 +- .../gmailapi/EncryptedReplyComposeGmailApiFlow.kt | 2 +- ...geWithOriginalAttachmentsComposeGmailApiFlow.kt | 2 +- ...geWithOriginalAttachmentsComposeGmailApiFlow.kt | 2 +- ...geWithOriginalAttachmentsComposeGmailApiFlow.kt | 2 +- .../StandardReplyAllComposeGmailApiFlow.kt | 2 +- .../gmailapi/StandardReplyComposeGmailApiFlow.kt | 2 +- .../response/model/AlternativeContentMsgBlock.kt | 2 +- .../com/flowcrypt/email/security/pgp/PgpMsg.kt | 14 ++++++++------ .../java/com/flowcrypt/email/ParcelableTest.kt | 4 +++- 12 files changed, 21 insertions(+), 17 deletions(-) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt index f9eb84c3f8..081b2c7595 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt @@ -168,7 +168,7 @@ class EncryptedForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFl hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ).toInitializationData( diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt index 2af6285a94..7c3f59cfb7 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt @@ -170,7 +170,7 @@ class EncryptedForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlo hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ).toInitializationData( diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyAllComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyAllComposeGmailApiFlow.kt index f9e0e9b992..5fb865ed44 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyAllComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyAllComposeGmailApiFlow.kt @@ -169,7 +169,7 @@ class EncryptedReplyAllComposeGmailApiFlow : BaseComposeGmailFlow() { hasSignedParts = true, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyComposeGmailApiFlow.kt index c780d72dae..17219a2ed7 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/EncryptedReplyComposeGmailApiFlow.kt @@ -170,7 +170,7 @@ class EncryptedReplyComposeGmailApiFlow : BaseComposeGmailFlow() { hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt index f84504d522..852e98e09a 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlow.kt @@ -176,7 +176,7 @@ class StandardForwardOfEncryptedMessageWithOriginalAttachmentsComposeGmailApiFlo hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ).toInitializationData( diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedPgpMimeMessageWithOriginalAttachmentsComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedPgpMimeMessageWithOriginalAttachmentsComposeGmailApiFlow.kt index 32c877e8c0..f3dd5929ef 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedPgpMimeMessageWithOriginalAttachmentsComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfEncryptedPgpMimeMessageWithOriginalAttachmentsComposeGmailApiFlow.kt @@ -170,7 +170,7 @@ class StandardForwardOfEncryptedPgpMimeMessageWithOriginalAttachmentsComposeGmai hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ).toInitializationData( diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt index 5b3741df98..1e7333e5d2 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow.kt @@ -166,7 +166,7 @@ class StandardForwardOfStandardMessageWithOriginalAttachmentsComposeGmailApiFlow hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ).toInitializationData( diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyAllComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyAllComposeGmailApiFlow.kt index dbbb575c8f..a1b994f980 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyAllComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyAllComposeGmailApiFlow.kt @@ -151,7 +151,7 @@ class StandardReplyAllComposeGmailApiFlow : BaseComposeGmailFlow() { hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyComposeGmailApiFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyComposeGmailApiFlow.kt index d4ee4c550c..3214e952dc 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyComposeGmailApiFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/StandardReplyComposeGmailApiFlow.kt @@ -165,7 +165,7 @@ class StandardReplyComposeGmailApiFlow : BaseComposeGmailFlow() { hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt index 7c0aa36701..6d35b0435f 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt @@ -15,7 +15,7 @@ import kotlinx.parcelize.Parcelize @Parcelize data class AlternativeContentMsgBlock( @Expose override val error: MsgBlockError? = null, - @Expose val plainVersionBlock: MsgBlock, + @Expose val plainBlocks: List, @Expose val otherBlocks: List, @Expose override val isOpenPGPMimeSigned: Boolean ) : MsgBlock { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt index b04fad4a80..90984c0fed 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt @@ -362,13 +362,13 @@ object PgpMsg { val partWithPlainText = parts.firstOrNull { it.isPlainText() } if (partWithPlainText != null) { - val plainVersionBlock = extractMsgBlocksFromPart( + val plainBlocks = extractMsgBlocksFromPart( part = partWithPlainText, verificationPublicKeys = verificationPublicKeys, secretKeys = secretKeys, protector = protector, isOpenPGPMimeSigned = isOpenPGPMimeSigned - ).firstOrNull() + ).toList() val otherBlocks = (parts - partWithPlainText).flatMap { alternativePart -> extractMsgBlocksFromPart( @@ -380,10 +380,10 @@ object PgpMsg { ) } - if (plainVersionBlock != null) { + if (plainBlocks.isNotEmpty()) { blocks.add( AlternativeContentMsgBlock( - plainVersionBlock = plainVersionBlock, + plainBlocks = plainBlocks, otherBlocks = otherBlocks, isOpenPGPMimeSigned = isOpenPGPMimeSigned ) @@ -658,7 +658,7 @@ object PgpMsg { .toFactory() val cleanHtml1 = policyFactory.sanitize(originalDocument.html()) - val document = Jsoup.parse(cleanHtml1) + val document = Jsoup.parse(cleanHtml1, "", Parser.xmlParser()) document.outputSettings().prettyPrint(false) moveElementsOutOfAnchorTag(document) @@ -1612,10 +1612,12 @@ object PgpMsg { isOpenPGPMimeSigned = false ) ), - plainVersionBlock = MsgBlockFactory.fromContent( + plainBlocks = listOf( + MsgBlockFactory.fromContent( MsgBlock.Type.DECRYPTED_TEXT, decryptedText, isOpenPGPMimeSigned = false + ) ), isOpenPGPMimeSigned = false ) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt index 15a6e0698f..dc62242d27 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt @@ -105,10 +105,12 @@ class ParcelableTest(val name: String, private val currentClass: Class Date: Wed, 12 Mar 2025 10:42:36 +0200 Subject: [PATCH 11/13] wip --- ...ltipart_alternative_pgp_in_text_plain.json | 32 +-- ...ageTestRecipientsDuringReplyAllFlowTest.kt | 2 +- .../email/ui/MessageDetailsFlowTest.kt | 28 ++- .../model/AlternativeContentMsgBlock.kt | 4 + .../response/model/VerificationResult.kt | 4 +- .../flowcrypt/email/security/pgp/PgpMsg.kt | 221 ++++++++++++------ 6 files changed, 197 insertions(+), 94 deletions(-) diff --git a/FlowCrypt/src/androidTest/assets/messages/info/encrypted_msg_info_multipart_alternative_pgp_in_text_plain.json b/FlowCrypt/src/androidTest/assets/messages/info/encrypted_msg_info_multipart_alternative_pgp_in_text_plain.json index 597050ff14..d80fd61244 100644 --- a/FlowCrypt/src/androidTest/assets/messages/info/encrypted_msg_info_multipart_alternative_pgp_in_text_plain.json +++ b/FlowCrypt/src/androidTest/assets/messages/info/encrypted_msg_info_multipart_alternative_pgp_in_text_plain.json @@ -1,16 +1,30 @@ { "encryptionType": "ENCRYPTED", + "localFolder": { + "account": "default@flowcrypt.test", + "attributes": [ + "\\HasNoChildren" + ], + "folderAlias": "INBOX", + "fullName": "INBOX", + "isAll": false, + "isCustom": false, + "isDrafts": false, + "isOutbox": false, + "labelListVisibility": "SHOW", + "msgCount": 1 + }, "msgBlocks": [ { - "content": " \u003c!DOCTYPE html\u003e\n \u003chtml\u003e\n \u003chead\u003e\n \u003cmeta name\u003d\"viewport\" content\u003d\"width\u003ddevice-width\"/\u003e\n \u003cstyle\u003e\n body { word-wrap: break-word; word-break: break-word; hyphens: auto; margin-left: 0px; padding-left: 0px; }\n body img { display: inline !important; height: auto !important; max-width: 95% !important; }\n body pre { white-space: pre-wrap !important; }\n body \u003e div.MsgBlock \u003e table { zoom: 75% } /* table layouts tend to overflow - eg emails from fb */\n \u003c/style\u003e\n \u003c/head\u003e\n \u003cbody\u003e\u003cdiv class\u003d\"MsgBlock GREEN\" style\u003d\"padding-left: 8px;min-height: 50px;padding-top: 4px;padding-bottom: 4px;width: 100%;border: 1px solid #f0f0f0;border-left: 8px solid #31A217;border-right: none;background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAMAAAAPdrEwAAAAh1BMVEXw8PD////w8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PD7MuHIAAAALXRSTlMAAAECBAcICw4QEhUZIyYqMTtGTV5kdn2Ii5mfoKOqrbG0uL6/xcnM0NTX2t1l7cN4AAAB0UlEQVR4Ae3Y3Y4SQRCG4bdHweFHRBTBH1FRFLXv//qsA8kmvbMdXhh2Q0KfknpSCQc130c67s22+e9+v/+d84fxkSPH0m/+5P9vN7vRV0vPfx7or1NB23e99KAHuoXOOc6moQsBwNN1Q9g4Wdh1uq3MA7Qn0+2ylAt7WbWpyT+Wo8roKH6v2QhZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2gjZ2AUNOLmwgQdogEJ2dnF3UJdU3WjqO/u96aYtVd/7jqvIyu76G5se6GaY7tNNcy5d7se7eWVnDz87fMkuVuS8epF6f9NPObPY5re9y4N1/vya9Gr3se2bfvl9M0mkyZdv077p+a/3z4Meby5Br4NWiV51BaiUqfLro9I3WiR61RVcffwfXI7u5zZ20EOA82Uu8x3SlrSwXQuBSvSqK0AletUVoBK96gpIwlZy0MJWctDCVnLQwlZy0MJWctDCVnLQwlZy0MJWctDCVnLQwlZy0MJWctDCVnLQwlZy0MJWckIletUVIJJxITN6wtZd2EI+0NquyIJOnUpFVvRpcwmV6FVXgEr0qitAJXrVFaASveoKUIledQWoRK+6AlSiV13BP+/VVbky7Xq1AAAAAElFTkSuQmCC);\"\u003e\u003chtml\u003e\u003chead\u003e\u003c/head\u003e\u003cbody\u003eEncrypted text\u003c/body\u003e\u003c/html\u003e\u003c/div\u003e\u003c!-- next MsgBlock --\u003e\n\u003cdiv class\u003d\"MsgBlock PLAIN\" style\u003d\"padding-left: 8px;min-height: 50px;padding-top: 4px;padding-bottom: 4px;width: 100%;border: none;\"\u003e\u003chtml\u003e\u003chead\u003e\u003c/head\u003e\u003cbody\u003eFooter with some text\u003c/body\u003e\u003c/html\u003e\u003c/div\u003e\u003c!-- next MsgBlock --\u003e\n\u003c/body\u003e\n \u003c/html\u003e", + "content": " \u003c!DOCTYPE html\u003e\n \u003chtml\u003e\n \u003chead\u003e\n \u003cmeta name\u003d\"viewport\" content\u003d\"width\u003ddevice-width\"\u003e\n \u003cstyle\u003e\n body { word-wrap: break-word; word-break: break-word; hyphens: auto; margin-left: 0px; padding-left: 0px; }\n blockquote { border-left: 1px solid #CCCCCC; margin: 0px 0px 0px 10px; padding:10px 0px 0px 10px; }\n body img { display: inline !important; height: auto !important; max-width: 95% !important; }\n details \u003e summary { list-style-type: none; }\n details \u003e summary::-webkit-details-marker { display: none; }\n details \u003e summary::before { content: \u0027▪▪▪\u0027; color: #31a217; border: 2px solid; border-radius: 5px; padding: 0px 5px 0px 5px; font-size: 75%; }\n summary:active:before { opacity: 0.5; }\n body pre { white-space: pre-wrap !important; }\n body \u003e div.MsgBlock \u003e table { zoom: 75% } /* table layouts tend to overflow - eg emails from fb */\n @media (prefers-color-scheme: dark) {\n .MsgBlock.GREEN { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAMAAAAPdrEwAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJcEhZcwAACxMAAAsTAQCanBgAAABCUExURUdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHPTtPsAAAAWdFJOUwAQbDIRQwiFQnQhUxl9SilkOzpLa2NiUIRSAAABRElEQVRYw+3ZTXOEMAgGYIwhEExstx///6/2sJ09roBy6Jb34MXhGZ1dCUYAXZgeYbg2Sb8EPdZHBmQyfzlre37+zS0jPa8VQre8Ht2V00ZFs/DZqGpDHw4blQ2OzTaqW6fVRkNTttloavcWG40Lid5G8xKltdGx+Onsm2tZ1dirc8FmugXJAHzQceTEkMEkQR34XG0mc232oL+j7ED0GSFvRDDK4bjhmbuoDAChLeKiBwDUGHoB4J0Ky8U/IRfaGSLe1+5vgkm/Bi0iIvVauoqICGQymROP0fsSVVt799qHpW5bUei0VWUuW1nksJu2pPbebHJRX0ztpcXIRtskm2yjbLDNstp2yErbJavsVqZvd7/OI3ub3u8GYz4fU2T6v0i0k+czmX8+/8y5LSFyp69v6hFP4E78e4jZhwjZhrjvQ1joHzaDDNBRxE1iAAAAAElFTkSuQmCC); }\n }\n \n @media (prefers-color-scheme: light) {\n .MsgBlock.GREEN { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAMAAAAPdrEwAAAAh1BMVEXw8PD////w8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PD7MuHIAAAALXRSTlMAAAECBAcICw4QEhUZIyYqMTtGTV5kdn2Ii5mfoKOqrbG0uL6/xcnM0NTX2t1l7cN4AAAB0UlEQVR4Ae3Y3Y4SQRCG4bdHweFHRBTBH1FRFLXv//qsA8kmvbMdXhh2Q0KfknpSCQc130c67s22+e9+v/+d84fxkSPH0m/+5P9vN7vRV0vPfx7or1NB23e99KAHuoXOOc6moQsBwNN1Q9g4Wdh1uq3MA7Qn0+2ylAt7WbWpyT+Wo8roKH6v2QhZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2ghZ2gjZ2AUNOLmwgQdogEJ2dnF3UJdU3WjqO/u96aYtVd/7jqvIyu76G5se6GaY7tNNcy5d7se7eWVnDz87fMkuVuS8epF6f9NPObPY5re9y4N1/vya9Gr3se2bfvl9M0mkyZdv077p+a/3z4Meby5Br4NWiV51BaiUqfLro9I3WiR61RVcffwfXI7u5zZ20EOA82Uu8x3SlrSwXQuBSvSqK0AletUVoBK96gpIwlZy0MJWctDCVnLQwlZy0MJWctDCVnLQwlZy0MJWctDCVnLQwlZy0MJWctDCVnLQwlZy0MJWckIletUVIJJxITN6wtZd2EI+0NquyIJOnUpFVvRpcwmV6FVXgEr0qitAJXrVFaASveoKUIledQWoRK+6AlSiV13BP+/VVbky7Xq1AAAAAElFTkSuQmCC); }\n }\n \u003c/style\u003e\n \u003c/head\u003e\n \u003cbody\u003e\u003cdiv class\u003d\"MsgBlock GREEN\" style\u003d\"padding-left: 8px;min-height: 50px;padding-top: 4px;padding-bottom: 4px;width: 100%;border: 1px solid #f0f0f0;border-left: 8px solid #31A217;border-right: none;\"\u003eEncrypted text\u003c/div\u003e\u003c!-- next MsgBlock --\u003e\n\u003cdiv class\u003d\"MsgBlock PLAIN\" style\u003d\"padding-left: 8px;min-height: 50px;padding-top: 4px;padding-bottom: 4px;width: 100%;border: none;\"\u003eFooter with some text\u003c/div\u003e\u003c!-- next MsgBlock --\u003e\n\u003c/body\u003e\n \u003c/html\u003e", "isOpenPGPMimeSigned": false, "type": "plainHtml" } ], "msgEntity": { - "cc": [], "account": "default@flowcrypt.test", "accountType": "flowcrypt.test", + "cc": [], "flags": "\\SEEN", "folder": "INBOX", "from": [ @@ -25,12 +39,12 @@ "id": 149, "isDraft": false, "isEncrypted": true, + "isGmailThread": false, "isOutboxMsg": false, "isPasswordProtected": false, "isSeen": true, "msgState": "NONE", "receivedDate": 1671805307000, - "replyToAddresses": "Default User \u003cdefault@flowcrypt.test\u003e", "replyToAddress": [ { "address": "default@flowcrypt.test", @@ -38,6 +52,7 @@ "personal": "Default User" } ], + "replyToAddresses": "Default User \u003cdefault@flowcrypt.test\u003e", "sentDate": 1671805307000, "subject": "multipart/alternative. PGP message in text/plain", "to": [ @@ -58,16 +73,5 @@ "hasUnverifiedSignatures": false, "isPartialSigned": true, "keyIdOfSigningKeys": [] - }, - "localFolder": { - "account": "default@flowcrypt.test", - "attributes": [ - "\\HasNoChildren" - ], - "folderAlias": "INBOX", - "fullName": "INBOX", - "isCustom": false, - "msgCount": 25, - "labelListVisibility": "SHOW" } } diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/CreateMessageTestRecipientsDuringReplyAllFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/CreateMessageTestRecipientsDuringReplyAllFlowTest.kt index f91d38c2e1..ef6788e7ac 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/CreateMessageTestRecipientsDuringReplyAllFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/CreateMessageTestRecipientsDuringReplyAllFlowTest.kt @@ -260,7 +260,7 @@ class CreateMessageTestRecipientsDuringReplyAllFlowTest : BaseComposeScreenTest( hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt index e444a60076..5b01eb7a18 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt @@ -72,6 +72,7 @@ import com.flowcrypt.email.util.GeneralUtil import com.flowcrypt.email.util.PrivateKeysManager import com.flowcrypt.email.util.TestGeneralUtil import kotlinx.coroutines.runBlocking +import org.apache.commons.io.FilenameUtils import org.hamcrest.MatcherAssert import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.anything @@ -1081,7 +1082,12 @@ class MessageDetailsFlowTest : BaseMessageDetailsFlowTest() { accountEntity = addAccountToDatabaseRule.accountEntityWithDecryptedInfo ) - val attachmentName = "thumb_up.png" + val attachmentName = requireNotNull((msgInfo?.msgBlocks?.first { + it.type == MsgBlock.Type.DECRYPTED_ATT + } as DecryptedAttMsgBlock).attMeta.name) + + val baseAttachmentName = FilenameUtils.getBaseName(attachmentName) + val downloadCompleteLabel = getResString(R.string.download_complete) val uiAutomatorTimeout = 5000L @@ -1097,19 +1103,21 @@ class MessageDetailsFlowTest : BaseMessageDetailsFlowTest() { .check(matches(isDisplayed())) .perform(click()) - //Unfortunately, due to the Scoped Storage, - //we don't have direct access to the file system and we can't check that a new file was created. + //we don't have direct access to the file system and we can't check that a new file was created + //(Also we can't use the full original name because there can be + //an existing file with the same name). + //So we will use just a base name. //That's why we will use UIAutomator to check that we have a notification //with text == "Download complete" val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.openNotification() //wait until we have a notification in the notification bar - device.wait(Until.hasObject(By.text(attachmentName)), uiAutomatorTimeout) + device.wait(Until.hasObject(By.textContains(baseAttachmentName)), uiAutomatorTimeout) //check that we have a notification with text == "Download complete" - val attachmentNameUiObject2 = device.findObject(By.text(attachmentName)) + val attachmentNameUiObject2 = device.findObject(By.textContains(baseAttachmentName)) + assertNotNull(attachmentNameUiObject2) val downloadCompleteLabelUiObject2 = device.findObject(By.text(downloadCompleteLabel)) - assertEquals(attachmentName, attachmentNameUiObject2.text) assertEquals(downloadCompleteLabel, downloadCompleteLabelUiObject2.text) device.pressHome() } @@ -1177,11 +1185,11 @@ class MessageDetailsFlowTest : BaseMessageDetailsFlowTest() { val attachmentMessageBlock = msgInfo?.msgBlocks?.get(2) as DecryptedAttMsgBlock - assertEquals(4, msgInfo.msgBlocks?.size) - assertEquals(MsgBlock.Type.PLAIN_HTML, msgInfo.msgBlocks?.get(0)?.type) - assertEquals(MsgBlock.Type.ENCRYPTED_SUBJECT, msgInfo.msgBlocks?.get(1)?.type) + assertEquals(4, msgInfo.msgBlocks.size) + assertEquals(MsgBlock.Type.PLAIN_HTML, msgInfo.msgBlocks[0].type) + assertEquals(MsgBlock.Type.ENCRYPTED_SUBJECT, msgInfo.msgBlocks[1].type) assertEquals(MsgBlock.Type.DECRYPTED_ATT, attachmentMessageBlock.type) - assertEquals(MsgBlock.Type.PUBLIC_KEY, msgInfo.msgBlocks?.get(3)?.type) + assertEquals(MsgBlock.Type.PUBLIC_KEY, msgInfo.msgBlocks[3].type) baseCheck(msgInfo) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt index 6d35b0435f..7a72191722 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/AlternativeContentMsgBlock.kt @@ -26,4 +26,8 @@ data class AlternativeContentMsgBlock( @IgnoredOnParcel @Expose override val type: MsgBlock.Type = MsgBlock.Type.ALTERNATIVE + + @IgnoredOnParcel + val allBlocks: List + get() = plainBlocks + otherBlocks } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/VerificationResult.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/VerificationResult.kt index e603ac9aa4..e8b311f167 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/VerificationResult.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/VerificationResult.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.api.retrofit.response.model @@ -19,7 +19,7 @@ data class VerificationResult( @Expose val hasSignedParts: Boolean, @Expose val hasMixedSignatures: Boolean, @Expose val isPartialSigned: Boolean, - @Expose val keyIdOfSigningKeys: List, + @Expose val keyIdOfSigningKeys: Set, @Expose val hasBadSignatures: Boolean, ) : Parcelable { @IgnoredOnParcel diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt index 90984c0fed..60343563b3 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpMsg.kt @@ -837,7 +837,7 @@ object PgpMsg { var signedBlockCount = 0 var isPartialSigned = false val verifiedSignatures = mutableListOf() - val keyIdOfSigningKeys = mutableListOf() + val keyIdOfSigningKeys = mutableSetOf() for (block in msgBlocks) { // We don't need Base64 correction here, fromAttachment() does this for us @@ -846,51 +846,39 @@ object PgpMsg { // So, at least meanwhile, not porting this: // block.content = isContentBlock(block.type) // ? block.content.toUtfStr() : block.content.toRawBytesStr(); - if (block.type in MsgBlock.Type.SIGNED_BLOCK_TYPES) { - val messageMetadata = when (block) { - is DecryptedAndOrSignedContentMsgBlock -> { - block.messageMetadata - } - is SignedMsgBlock -> { - block.openPgpMetadata + filterBlocksViaTree(listOf(block)) { innerBlock -> + innerBlock.type in MsgBlock.Type.SIGNED_BLOCK_TYPES + }.forEach { pgpBlock -> + analyzeBlockForPgp(pgpBlock) { hasEncryptedContent, + hasSignedContent, + hasInvalidSignatures, + keyIdsOfSigningKeys, + verifiedSignaturesList -> + if (!isEncrypted) { + isEncrypted = hasEncryptedContent } - else -> null - } - - if (!isEncrypted) { - isEncrypted = messageMetadata?.isEncrypted == true - } - - if (messageMetadata?.isSigned == true) { - signedBlockCount++ - - if (messageMetadata.rejectedInlineSignatures.isNotEmpty() - || messageMetadata.rejectedDetachedSignatures.isNotEmpty() - ) { - val invalidSignatureFailures = messageMetadata.rejectedInlineSignatures + - messageMetadata.rejectedDetachedSignatures - - hasBadSignatures = invalidSignatureFailures.any { - it.validationException.underlyingException != null - } + if (hasSignedContent) { + signedBlockCount++ + } - keyIdOfSigningKeys.addAll(invalidSignatureFailures.filter { - it.validationException.message?.matches("Missing verification key.?".toRegex()) == true - }.map { it.signature.keyID }) + if (!hasBadSignatures) { + hasBadSignatures = hasInvalidSignatures } + keyIdOfSigningKeys.addAll(keyIdsOfSigningKeys) + if (verifiedSignatures.isEmpty()) { - verifiedSignatures.addAll(messageMetadata.verifiedSignatures) + verifiedSignatures.addAll(verifiedSignaturesList) } else { val keyIdsOfAllVerifiedSignatures = verifiedSignatures.map { it.signingKey.keyId } - val keyIdsOfCurrentVerifiedSignatures = messageMetadata.verifiedSignatures.map { + val keyIdsOfCurrentVerifiedSignatures = verifiedSignaturesList.map { it.signingKey.keyId } if (keyIdsOfAllVerifiedSignatures != keyIdsOfCurrentVerifiedSignatures) { hasMixedSignatures = true - verifiedSignatures.addAll(messageMetadata.verifiedSignatures) + verifiedSignatures.addAll(verifiedSignaturesList) } } } @@ -957,6 +945,65 @@ object PgpMsg { ) } + private fun analyzeBlockForPgp( + block: MsgBlock, + action: ( + hasEncryptedContent: Boolean, + hasSignedContent: Boolean, + hasInvalidSignatures: Boolean, + keyIdsOfSigningKeys: Set, + verifiedSignaturesList: List + ) -> Unit + ) { + var hasSignedContent = false + var hasEncryptedContent = false + var hasInvalidSignatures = false + val keyIdsOfSigningKeys = mutableSetOf() + + if (block.type in MsgBlock.Type.SIGNED_BLOCK_TYPES) { + val messageMetadata = when (block) { + is DecryptedAndOrSignedContentMsgBlock -> { + block.messageMetadata + } + + is SignedMsgBlock -> { + block.openPgpMetadata + } + + else -> null + } + + hasEncryptedContent = messageMetadata?.isEncrypted == true + + if (messageMetadata?.isSigned == true) { + hasSignedContent = true + + if (messageMetadata.rejectedInlineSignatures.isNotEmpty() + || messageMetadata.rejectedDetachedSignatures.isNotEmpty() + ) { + val invalidSignatureFailures = messageMetadata.rejectedInlineSignatures + + messageMetadata.rejectedDetachedSignatures + + hasInvalidSignatures = invalidSignatureFailures.any { + it.validationException.underlyingException != null + } + + keyIdsOfSigningKeys.addAll(invalidSignatureFailures.filter { + it.validationException.message?.matches("Missing verification key.?".toRegex()) == true + }.map { it.signature.keyID }) + } + } + + action.invoke( + hasEncryptedContent, + hasSignedContent, + hasInvalidSignatures, + keyIdsOfSigningKeys, + messageMetadata?.verifiedSignatures ?: emptyList() + ) + } + } + private fun extractInnerBlocks( decryptedAndOrSignedContentMsgBlock: DecryptedAndOrSignedContentMsgBlock ): Collection { @@ -1196,7 +1243,8 @@ object PgpMsg { } private fun prepareFormattedContentBlock( - allContentBlocks: List + allContentBlocks: List, + stripHtmlRootTags: Boolean = false ): FormattedContentBlockResult { val inlineImagesByCid = mutableMapOf() val imagesAtTheBottom = mutableListOf() @@ -1235,6 +1283,40 @@ object PgpMsg { for (block in allContentBlocks.filterNot { MimeUtils.isPlainImgAtt(it) }) { when (block) { is AlternativeContentMsgBlock -> { + if (block.plainBlocks.size > 1) { + prepareFormattedContentBlock( + allContentBlocks = block.plainBlocks, + stripHtmlRootTags = true + ).apply { + msgContentAsHtml.append(contentBlock.content) + msgContentAsText.append(text).append('\n') + } + + //we skip otherBlocks if we have more than one plain block + continue + } else { + val singlePlainBlock = block.plainBlocks.first() + val singlePlainVersionHasDecryptedContent = + singlePlainBlock is DecryptedAndOrSignedContentMsgBlock + if (singlePlainVersionHasDecryptedContent) { + prepareFormattedContentBlock( + allContentBlocks = singlePlainBlock.blocks, + stripHtmlRootTags = true + ).apply { + msgContentAsHtml.append(contentBlock.content) + msgContentAsText.append(text).append('\n') + } + //we skip otherBlocks if plain version has decrypted content + continue + } else { + collectDataFromMsgBlock( + block = singlePlainBlock, + useHtml = false, + usePlainText = true + ).invoke() + } + } + val htmlVersionBlock = block.otherBlocks.firstOrNull() htmlVersionBlock?.let { htmlBlock -> collectDataFromMsgBlock( @@ -1243,11 +1325,16 @@ object PgpMsg { usePlainText = false ).invoke() } - collectDataFromMsgBlock( - block = block.plainVersionBlock, - useHtml = false, - usePlainText = true - ).invoke() + } + + is DecryptedAndOrSignedContentMsgBlock -> { + prepareFormattedContentBlock( + allContentBlocks = block.blocks, + stripHtmlRootTags = true + ).apply { + msgContentAsHtml.append(contentBlock.content) + msgContentAsText.append(text).append('\n') + } } else -> { @@ -1277,33 +1364,33 @@ object PgpMsg { text = msgContentAsText.toString().trim(), contentBlock = MsgBlockFactory.fromContent( type = MsgBlock.Type.PLAIN_HTML, - """ - - - - - - - $msgContentAsHtml - - """.trimIndent(), isOpenPGPMimeSigned = false + content = msgContentAsHtml.toString().takeIf { stripHtmlRootTags } ?: """ + + + + + + + $msgContentAsHtml + + """.trimIndent(), isOpenPGPMimeSigned = false ) ) } @@ -1316,7 +1403,7 @@ object PgpMsg { blocks.forEach { block -> when { block is AlternativeContentMsgBlock -> { - addAll(filterBlocksViaTree(block.otherBlocks, predicate)) + addAll(filterBlocksViaTree(block.allBlocks, predicate)) } predicate(block) -> { From 42baacbe9a73a75bc3119f05ebe57dddd2caa16f Mon Sep 17 00:00:00 2001 From: denbond7 Date: Wed, 12 Mar 2025 11:26:24 +0200 Subject: [PATCH 12/13] wip --- .../email/extensions/kotlin/StringExt.kt | 2 +- .../email/api/email/EmailUtilTest.kt | 2 +- .../email/security/pgp/PgpMsgTest.kt | 21 ++++++++++++++++--- ...alert-20210416-084836-UTC-html-content.txt | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt index 90a413da45..1aa3b38223 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/kotlin/StringExt.kt @@ -67,7 +67,7 @@ fun String.toEscapedHtml(): String { .replace("<", "<") .replace(">", ">") .replace("/", "/") - .replace("\n", "
") + .replace("\n", "
") } fun String.unescapeHtml(): String { diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/api/email/EmailUtilTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/api/email/EmailUtilTest.kt index df899def40..1097c7a9d6 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/api/email/EmailUtilTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/api/email/EmailUtilTest.kt @@ -119,7 +119,7 @@ class EmailUtilTest { hasSignedParts = false, hasMixedSignatures = false, isPartialSigned = false, - keyIdOfSigningKeys = emptyList(), + keyIdOfSigningKeys = emptySet(), hasBadSignatures = false ) ) diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt index 41171816af..8ea038b4c7 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt @@ -413,7 +413,11 @@ class PgpMsgTest { assertEquals(1, result.blocks.size) val block = result.blocks[0] assertEquals(MsgBlock.Type.PLAIN_HTML, block.type) - checkRenderedBlock(block, listOf(RenderedBlock.normal(true, "PLAIN", TEXT_SPECIAL_CHARS))) + + checkRenderedBlock( + block, + listOf(RenderedBlock.normal(true, "PLAIN", HTML_SPECIAL_CHARS)) + ) } @Test @@ -437,7 +441,11 @@ class PgpMsgTest { assertEquals(1, result.blocks.size) val block = result.blocks[0] assertEquals(MsgBlock.Type.PLAIN_HTML, block.type) - checkRenderedBlock(block, listOf(RenderedBlock.normal(true, "PLAIN", HTML_SPECIAL_CHARS))) + + checkRenderedBlock( + block, + listOf(RenderedBlock.normal(true, "PLAIN", HTML_SPECIAL_CHARS)) + ) } @Test @@ -460,7 +468,11 @@ class PgpMsgTest { assertEquals(1, result.blocks.size) val block = result.blocks[0] assertEquals(MsgBlock.Type.PLAIN_HTML, block.type) - checkRenderedBlock(block, listOf(RenderedBlock.normal(true, "GREEN", HTML_SPECIAL_CHARS))) + + checkRenderedBlock( + block, + listOf(RenderedBlock.normal(true, "PLAIN", HTML_SPECIAL_CHARS)) + ) } @Test @@ -479,6 +491,7 @@ class PgpMsgTest { } @Test + @Ignore("need to fix") fun testParseDecryptMsgPlainInlineImage() { val text = loadResourceAsString("other/plain-inline-image.txt") val keys = TestKeys.KEYS["rsa1"]!!.listOfKeysWithPassPhrase @@ -493,6 +506,7 @@ class PgpMsgTest { val block = result.blocks[0] assertEquals(MsgBlock.Type.PLAIN_HTML, block.type) val htmlContent = loadResourceAsString("other/plain-inline-image-html-content.txt") + checkRenderedBlock(block, listOf(RenderedBlock.normal(true, "PLAIN", htmlContent))) } @@ -529,6 +543,7 @@ class PgpMsgTest { val htmlContent = loadResourceAsString( "other/plain-google-security-alert-20210416-084836-UTC-html-content.txt" ) + checkRenderedBlock(block, listOf(RenderedBlock.normal(true, "PLAIN", htmlContent))) } diff --git a/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-html-content.txt b/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-html-content.txt index 035903bb90..3f9f8d7643 100644 --- a/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-html-content.txt +++ b/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-google-security-alert-20210416-084836-UTC-html-content.txt @@ -1 +1 @@ -[remote content blocked for your privacy]

FlowCrypt iOS App was granted access to your Google Account
[img]flowcrypt.compatibility@gmail.com
[img]
If you did not grant access, you should check this activity and secure your account.
[img]
You can also see security activity at
https://myaccount.google.com/notifications
You received this email to let you know about important changes to your Google Account and services.
© 2021 Google LLC, 1600 Amphitheatre Parkway, Mountain View, CA 94043, USA
\ No newline at end of file +[remote content blocked for your privacy]

FlowCrypt iOS App was granted access to your Google Account
[img]flowcrypt.compatibility@gmail.com

If you did not grant access, you should check this activity and secure your account.
You can also see security activity at
https://myaccount.google.com/notifications
[img]
You received this email to let you know about important changes to your Google Account and services.
© 2021 Google LLC, 1600 Amphitheatre Parkway, Mountain View, CA 94043, USA
\ No newline at end of file From 2f040cf72e8fd652b35552af6b3abbb90b0296cf Mon Sep 17 00:00:00 2001 From: denbond7 Date: Wed, 12 Mar 2025 13:22:09 +0200 Subject: [PATCH 13/13] wip --- .../java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt | 1 + .../test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt | 1 - .../PgpMsgTest/other/plain-inline-image-html-content.txt | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt index 5b01eb7a18..ada4d3077a 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/MessageDetailsFlowTest.kt @@ -1118,6 +1118,7 @@ class MessageDetailsFlowTest : BaseMessageDetailsFlowTest() { val attachmentNameUiObject2 = device.findObject(By.textContains(baseAttachmentName)) assertNotNull(attachmentNameUiObject2) val downloadCompleteLabelUiObject2 = device.findObject(By.text(downloadCompleteLabel)) + assertNotNull(downloadCompleteLabelUiObject2) assertEquals(downloadCompleteLabel, downloadCompleteLabelUiObject2.text) device.pressHome() } diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt index 8ea038b4c7..c406a85de5 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpMsgTest.kt @@ -491,7 +491,6 @@ class PgpMsgTest { } @Test - @Ignore("need to fix") fun testParseDecryptMsgPlainInlineImage() { val text = loadResourceAsString("other/plain-inline-image.txt") val keys = TestKeys.KEYS["rsa1"]!!.listOfKeysWithPassPhrase diff --git a/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-inline-image-html-content.txt b/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-inline-image-html-content.txt index 55f72cebee..0dd221b325 100644 --- a/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-inline-image-html-content.txt +++ b/FlowCrypt/src/test/resources/PgpMsgTest/other/plain-inline-image-html-content.txt @@ -1 +1 @@ -
Below
image.png
Above
\ No newline at end of file +
Below
Above
\ No newline at end of file