diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt index 559e6d9140..ea0e0e3ffa 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt @@ -22,7 +22,6 @@ import androidx.test.espresso.matcher.RootMatchers.withDecorView import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread import androidx.test.platform.app.InstrumentationRegistry import com.flowcrypt.email.R @@ -40,7 +39,6 @@ import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.not import org.junit.After import org.junit.Before -import org.junit.runner.RunWith import java.io.InputStream import java.util.* import javax.mail.Session @@ -54,7 +52,6 @@ import javax.mail.internet.MimeMessage * Time: 16:37 * E-mail: DenBond7@gmail.com */ -@RunWith(AndroidJUnit4::class) abstract class BaseTest : BaseActivityTestImplementation { val roomDatabase: FlowCryptRoomDatabase = FlowCryptRoomDatabase.getDatabase(getTargetContext()) private var countingIdlingResource: IdlingResource? = null diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/CustomMatchers.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/CustomMatchers.kt index 61cab3ee61..66f6ec3fc7 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/CustomMatchers.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/CustomMatchers.kt @@ -7,6 +7,7 @@ package com.flowcrypt.email.matchers import android.view.View import android.widget.ListView +import androidx.annotation.DrawableRes import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Root import androidx.test.espresso.matcher.BoundedMatcher @@ -115,5 +116,12 @@ class CustomMatchers { fun isToast(): BaseMatcher { return ToastMatcher() } + + fun withTextViewDrawable( + @DrawableRes resourceId: Int, + @TextViewDrawableMatcher.DrawablePosition drawablePosition: Int + ): Matcher { + return TextViewDrawableMatcher(resourceId, drawablePosition) + } } } diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/TextViewDrawableMatcher.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/TextViewDrawableMatcher.kt new file mode 100644 index 0000000000..8a1a1eba91 --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/matchers/TextViewDrawableMatcher.kt @@ -0,0 +1,57 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.matchers + +import android.view.View +import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.annotation.IntDef +import androidx.core.graphics.drawable.toBitmap +import org.hamcrest.Description +import org.hamcrest.TypeSafeMatcher + +/** + * @author Denis Bondarenko + * Date: 1/14/22 + * Time: 11:55 AM + * E-mail: DenBond7@gmail.com + */ +class TextViewDrawableMatcher( + @DrawableRes private val resourceId: Int, + @DrawablePosition private val drawablePosition: Int +) : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText( + "TextView with compound drawable at position $drawablePosition" + + " same as drawable with id $resourceId" + ) + } + + override fun matchesSafely(view: View): Boolean { + if (view !is TextView) { + return false + } + + val expectedBitmap = view.context.getDrawable(resourceId)?.toBitmap() + return view.compoundDrawables[drawablePosition].toBitmap().sameAs(expectedBitmap) + } + + @Retention(AnnotationRetention.SOURCE) + @IntDef( + DrawablePosition.LEFT, + DrawablePosition.TOP, + DrawablePosition.RIGHT, + DrawablePosition.BOTTOM + ) + annotation class DrawablePosition { + companion object { + const val LEFT = 0 + const val TOP = 1 + const val RIGHT = 2 + const val BOTTOM = 3 + } + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AddOtherAccountFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AddOtherAccountFragmentTest.kt index d632dbaa81..1bcd16f4fe 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AddOtherAccountFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AddOtherAccountFragmentTest.kt @@ -79,8 +79,8 @@ class AddOtherAccountFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AttesterSettingsFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AttesterSettingsFragmentTest.kt index 8f0cd42a95..72c12c70a0 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AttesterSettingsFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/AttesterSettingsFragmentTest.kt @@ -55,9 +55,9 @@ class AttesterSettingsFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(accountRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoKeysTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoKeysTest.kt index 2ce182d479..486f5f6f3f 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoKeysTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoKeysTest.kt @@ -37,9 +37,9 @@ class BackupKeysFragmentNoKeysTest : BaseBackupKeysFragmentTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoPrvBackupOrgRuleTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoPrvBackupOrgRuleTest.kt index b289390538..4bf7d2078d 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoPrvBackupOrgRuleTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentNoPrvBackupOrgRuleTest.kt @@ -53,9 +53,9 @@ class BackupKeysFragmentNoPrvBackupOrgRuleTest : BaseBackupKeysFragmentTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyPassphraseInRamTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyPassphraseInRamTest.kt index b424404d98..e97f6f41da 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyPassphraseInRamTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyPassphraseInRamTest.kt @@ -51,10 +51,10 @@ class BackupKeysFragmentSingleKeyPassphraseInRamTest : BaseBackupKeysFragmentTes @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyWeakPassphraseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyWeakPassphraseTest.kt index d0e6fea67f..ee37101662 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyWeakPassphraseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentSingleKeyWeakPassphraseTest.kt @@ -42,7 +42,8 @@ class BackupKeysFragmentSingleKeyWeakPassphraseTest : BaseBackupKeysFragmentTest @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around( AddPrivateKeyToDatabaseRule( @@ -52,7 +53,6 @@ class BackupKeysFragmentSingleKeyWeakPassphraseTest : BaseBackupKeysFragmentTest sourceType = KeyImportDetails.SourceType.EMAIL ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTest.kt index 852c0bffdc..e8448c4b6c 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTest.kt @@ -40,10 +40,10 @@ class BackupKeysFragmentTest : BaseBackupKeysFragmentTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphraseInRamTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphraseInRamTest.kt index bec8b4e42e..1ff839bfec 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphraseInRamTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphraseInRamTest.kt @@ -50,7 +50,8 @@ class BackupKeysFragmentTwoKeysDiffPassphraseInRamTest : BaseBackupKeysFragmentT @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) .around( @@ -62,7 +63,6 @@ class BackupKeysFragmentTwoKeysDiffPassphraseInRamTest : BaseBackupKeysFragmentT passphraseType = KeyEntity.PassphraseType.RAM ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphrasesTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphrasesTest.kt index fe05926579..a509959b97 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphrasesTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysDiffPassphrasesTest.kt @@ -43,7 +43,8 @@ class BackupKeysFragmentTwoKeysDiffPassphrasesTest : BaseBackupKeysFragmentTest( @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule()) .around( @@ -54,7 +55,6 @@ class BackupKeysFragmentTwoKeysDiffPassphrasesTest : BaseBackupKeysFragmentTest( sourceType = KeyImportDetails.SourceType.EMAIL ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysSamePassphraseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysSamePassphraseTest.kt index 2938417e96..dacd20ce41 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysSamePassphraseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/BackupKeysFragmentTwoKeysSamePassphraseTest.kt @@ -42,7 +42,8 @@ class BackupKeysFragmentTwoKeysSamePassphraseTest : BaseBackupKeysFragmentTest() @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule()) .around( @@ -53,7 +54,6 @@ class BackupKeysFragmentTwoKeysSamePassphraseTest : BaseBackupKeysFragmentTest() sourceType = KeyImportDetails.SourceType.EMAIL ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ChangePassphraseOfImportedKeysFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ChangePassphraseOfImportedKeysFragmentTest.kt index 720b33c494..c9a0535eeb 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ChangePassphraseOfImportedKeysFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ChangePassphraseOfImportedKeysFragmentTest.kt @@ -55,10 +55,10 @@ class ChangePassphraseOfImportedKeysFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityTestMultiBackups.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityTestMultiBackups.kt index 3bff37ca23..90a85091fb 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityTestMultiBackups.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityTestMultiBackups.kt @@ -52,8 +52,8 @@ class CheckKeysActivityTestMultiBackups : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithExistingKeysTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithExistingKeysTest.kt index 58a25d5d34..2dff046350 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithExistingKeysTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithExistingKeysTest.kt @@ -65,7 +65,8 @@ class CheckKeysActivityWithExistingKeysTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around( AddPrivateKeyToDatabaseRule( @@ -75,7 +76,6 @@ class CheckKeysActivityWithExistingKeysTest : BaseTest() { sourceType = KeyImportDetails.SourceType.EMAIL ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithoutExistingKeysTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithoutExistingKeysTest.kt index 31903390d6..4c258faae2 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithoutExistingKeysTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckKeysActivityWithoutExistingKeysTest.kt @@ -61,8 +61,8 @@ class CheckKeysActivityWithoutExistingKeysTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckPassphraseStrengthFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckPassphraseStrengthFragmentTest.kt index a67da573e9..51755bc531 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckPassphraseStrengthFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CheckPassphraseStrengthFragmentTest.kt @@ -33,7 +33,6 @@ import org.junit.Test import org.junit.rules.RuleChain import org.junit.rules.TestRule import org.junit.runner.RunWith -import java.util.Locale /** * @author Denis Bondarenko @@ -58,10 +57,10 @@ class CheckPassphraseStrengthFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) @@ -105,7 +104,7 @@ class CheckPassphraseStrengthFragmentTest : BaseTest() { .check(matches(isDisplayed())) .perform(replaceText(passPhrases[i])) onView(withId(R.id.tVPassphraseQuality)) - .check(matches(withText(startsWith(degreeOfReliabilityOfPassPhrase[i].toUpperCase(Locale.getDefault()))))) + .check(matches(withText(startsWith(degreeOfReliabilityOfPassPhrase[i].uppercase())))) onView(withId(R.id.eTPassphrase)) .check(matches(isDisplayed())) .perform(clearText()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyAllTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyAllTest.kt index 0c8149a8b0..32a2c88dcb 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyAllTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyAllTest.kt @@ -56,10 +56,10 @@ class CreateMessageActivityReplyAllTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule(account)) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyTest.kt index 3e2fd46faf..44e8e95158 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityReplyTest.kt @@ -46,10 +46,10 @@ class CreateMessageActivityReplyTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTest.kt index 829279269d..0517ddd42f 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTest.kt @@ -30,6 +30,7 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withParent import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest @@ -103,11 +104,11 @@ class CreateMessageActivityTest : BaseCreateMessageActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) .around(temporaryFolderRule) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) @@ -728,7 +729,7 @@ class CreateMessageActivityTest : BaseCreateMessageActivityTest() { private fun deleteAtt(att: File) { onView( allOf( - withId(R.id.imageButtonClearAtt), ViewMatchers.withParent( + withId(R.id.imageButtonClearAtt), withParent( allOf(withId(R.id.actionButtons), hasSibling(withText(att.name))) ) ) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTestPassInRamTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTestPassInRamTest.kt index 62460db1e7..61b48bb993 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTestPassInRamTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityTestPassInRamTest.kt @@ -50,10 +50,10 @@ class CreateMessageActivityTestPassInRamTest : BaseCreateMessageActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityWkdTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityWkdTest.kt index 734721d42c..59de52dfb2 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityWkdTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageActivityWkdTest.kt @@ -87,11 +87,11 @@ class CreateMessageActivityWkdTest : BaseCreateMessageActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(mockWebServerRule) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageFragmentPasswordProtectedTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageFragmentPasswordProtectedTest.kt new file mode 100644 index 0000000000..93c2718681 --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateMessageFragmentPasswordProtectedTest.kt @@ -0,0 +1,110 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.ui.activity + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.clearText +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.TestConstants +import com.flowcrypt.email.matchers.CustomMatchers.Companion.withTextViewDrawable +import com.flowcrypt.email.matchers.TextViewDrawableMatcher +import com.flowcrypt.email.rules.AddPrivateKeyToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import com.flowcrypt.email.ui.activity.base.BaseCreateMessageActivityTest +import org.hamcrest.Matchers.not +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +/** + * @author Denis Bondarenko + * Date: 1/14/22 + * Time: 11:29 AM + * E-mail: DenBond7@gmail.com + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +class CreateMessageFragmentPasswordProtectedTest : BaseCreateMessageActivityTest() { + private val addPrivateKeyToDatabaseRule = AddPrivateKeyToDatabaseRule() + private val temporaryFolderRule = TemporaryFolder() + + @get:Rule + var ruleChain: TestRule = RuleChain + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(temporaryFolderRule) + .around(activeActivityRule) + .around(ScreenshotTestRule()) + + @Test + fun testShowWebPortalPasswordButton() { + activeActivityRule?.launch(intent) + registerAllIdlingResources() + + onView(withId(R.id.editTextRecipientTo)) + .perform( + typeText(TestConstants.RECIPIENT_WITHOUT_PUBLIC_KEY_ON_ATTESTER), + closeSoftKeyboard() + ) + + //need to leave focus from 'To' field. move the focus to the next view + onView(withId(R.id.editTextEmailSubject)) + .perform(scrollTo(), click()) + + onView(withId(R.id.btnSetWebPortalPassword)) + .check(matches(isDisplayed())) + .check(matches(withText(getResString(R.string.tap_to_protect_with_web_portal_password)))) + .check( + matches( + withTextViewDrawable( + R.drawable.ic_password_not_protected_white_24, + TextViewDrawableMatcher.DrawablePosition.LEFT + ) + ) + ) + } + + @Test + fun testWebPortalPasswordButtonVisibility() { + activeActivityRule?.launch(intent) + registerAllIdlingResources() + + onView(withId(R.id.editTextRecipientTo)) + .perform( + typeText(TestConstants.RECIPIENT_WITHOUT_PUBLIC_KEY_ON_ATTESTER), + closeSoftKeyboard() + ) + //need to leave focus from 'To' field. move the focus to the next view + onView(withId(R.id.editTextEmailSubject)) + .perform(scrollTo(), click()) + onView(withId(R.id.editTextRecipientTo)) + .perform( + clearText(), typeText("some text"), clearText(), + ) + //need to leave focus from 'To' field. move the focus to the next view + onView(withId(R.id.editTextEmailSubject)) + .perform(scrollTo(), click()) + onView(withId(R.id.btnSetWebPortalPassword)) + .check(matches(not(isDisplayed()))) + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivityWithKeysTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivityWithKeysTest.kt index f9b939c0cc..b07e9b2ede 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivityWithKeysTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivityWithKeysTest.kt @@ -52,8 +52,8 @@ class CreateOrImportKeyActivityWithKeysTest : BaseCreateOrImportKeyActivityTest( @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreatePrivateKeyActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreatePrivateKeyActivityTest.kt index 3b42a87a14..11bd6db822 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreatePrivateKeyActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/CreatePrivateKeyActivityTest.kt @@ -49,8 +49,8 @@ class CreatePrivateKeyActivityTest : BasePassphraseActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/EmailManagerActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/EmailManagerActivityTest.kt index 343ed05b64..7ec15b7da4 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/EmailManagerActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/EmailManagerActivityTest.kt @@ -67,11 +67,11 @@ class EmailManagerActivityTest : BaseEmailListActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule(userWithoutLetters)) .around(AddAccountToDatabaseRule(userWithMoreThan21LettersAccount)) .around(AddLabelsToDatabaseRule(userWithMoreThan21LettersAccount, LOCAL_FOLDERS)) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/HtmlViewFromAssetsRawActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/HtmlViewFromAssetsRawActivityTest.kt index aa92512f2b..9682293be3 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/HtmlViewFromAssetsRawActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/HtmlViewFromAssetsRawActivityTest.kt @@ -45,8 +45,8 @@ class HtmlViewFromAssetsRawActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchForDomainTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchForDomainTest.kt index c41e5d9e41..8ba8b48a52 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchForDomainTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchForDomainTest.kt @@ -67,9 +67,9 @@ class ImportPgpContactActivityDisallowAttesterSearchForDomainTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchTest.kt index 7f8ecdb526..4a9d8160aa 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityDisallowAttesterSearchTest.kt @@ -67,9 +67,9 @@ class ImportPgpContactActivityDisallowAttesterSearchTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityTest.kt index 03d128e869..2d9272c670 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPgpContactActivityTest.kt @@ -105,10 +105,10 @@ class ImportPgpContactActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(mockWebServerRule) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityFromSettingsTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityFromSettingsTest.kt index 936cd48930..3b14715979 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityFromSettingsTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityFromSettingsTest.kt @@ -77,9 +77,9 @@ class ImportPrivateKeyActivityFromSettingsTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityNoPubOrgRulesTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityNoPubOrgRulesTest.kt index 0bcee69b89..02e5454385 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityNoPubOrgRulesTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPrivateKeyActivityNoPubOrgRulesTest.kt @@ -69,8 +69,8 @@ class ImportPrivateKeyActivityNoPubOrgRulesTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPublicKeyActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPublicKeyActivityTest.kt index ba627a9746..9fef782311 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPublicKeyActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ImportPublicKeyActivityTest.kt @@ -73,9 +73,9 @@ class ImportPublicKeyActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/LegalSettingsFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/LegalSettingsFragmentTest.kt index 4774f96397..6f5644d19a 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/LegalSettingsFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/LegalSettingsFragmentTest.kt @@ -49,9 +49,9 @@ class LegalSettingsFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityEkmTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityEkmTest.kt index de3e1c69e8..68fdb70ea0 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityEkmTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityEkmTest.kt @@ -64,10 +64,10 @@ class MessageDetailsActivityEkmTest : BaseMessageDetailsActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityPassphraseInRamTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityPassphraseInRamTest.kt index 6639026aef..9e584759da 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityPassphraseInRamTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityPassphraseInRamTest.kt @@ -54,10 +54,10 @@ class MessageDetailsActivityPassphraseInRamTest : BaseMessageDetailsActivityTest @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule(passphraseType = KeyEntity.PassphraseType.RAM)) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityTest.kt index 58944995b0..df2260da53 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/MessageDetailsActivityTest.kt @@ -98,10 +98,10 @@ class MessageDetailsActivityTest : BaseMessageDetailsActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PreviewImportPgpContactActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PreviewImportPgpContactActivityTest.kt index 1006919760..ddee7c0841 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PreviewImportPgpContactActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PreviewImportPgpContactActivityTest.kt @@ -48,9 +48,9 @@ class PreviewImportPgpContactActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PrivateKeysListFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PrivateKeysListFragmentTest.kt index 7ac1b975ac..2ec541ca5a 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PrivateKeysListFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PrivateKeysListFragmentTest.kt @@ -66,10 +66,10 @@ class PrivateKeysListFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PublicKeyDetailsFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PublicKeyDetailsFragmentTest.kt index bf6942035e..30a1b43b12 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PublicKeyDetailsFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/PublicKeyDetailsFragmentTest.kt @@ -81,7 +81,8 @@ class PublicKeyDetailsFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) .around( AddRecipientsToDatabaseRule( @@ -93,7 +94,6 @@ class PublicKeyDetailsFragmentTest : BaseTest() { ) ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecheckProvidedPassphraseFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecheckProvidedPassphraseFragmentTest.kt index 62e809ccd3..c0da55c830 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecheckProvidedPassphraseFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecheckProvidedPassphraseFragmentTest.kt @@ -56,10 +56,10 @@ class RecheckProvidedPassphraseFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecipientsListFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecipientsListFragmentTest.kt index 0b252fb731..e01c5a37a6 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecipientsListFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/RecipientsListFragmentTest.kt @@ -56,9 +56,9 @@ class RecipientsListFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentDefaultAccountTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentDefaultAccountTest.kt index a7fe9b3731..345a4e8293 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentDefaultAccountTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentDefaultAccountTest.kt @@ -44,10 +44,10 @@ class SearchBackupsInEmailFragmentDefaultAccountTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) .around(AddPrivateKeyToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentTest.kt index 42f726a962..c434174e31 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchBackupsInEmailFragmentTest.kt @@ -51,8 +51,8 @@ class SearchBackupsInEmailFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchMessagesActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchMessagesActivityTest.kt index 6ec2f66cd1..4fc67e7ea3 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchMessagesActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SearchMessagesActivityTest.kt @@ -70,9 +70,9 @@ class SearchMessagesActivityTest : BaseEmailListActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(accountRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SecuritySettingsFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SecuritySettingsFragmentTest.kt index b735ffe4cb..ac9c4d5a40 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SecuritySettingsFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SecuritySettingsFragmentTest.kt @@ -54,10 +54,10 @@ class SecuritySettingsFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SelectRecipientsActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SelectRecipientsActivityTest.kt index 6af486a7c1..22f9601a0e 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SelectRecipientsActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SelectRecipientsActivityTest.kt @@ -63,10 +63,10 @@ class SelectRecipientsActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) .around(AddRecipientsToDatabaseRule(CONTACTS)) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SettingsActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SettingsActivityTest.kt index 050668ce45..4d9f0d6c0d 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SettingsActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SettingsActivityTest.kt @@ -45,9 +45,9 @@ class SettingsActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ShareIntentsTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ShareIntentsTest.kt index 212e4f6840..5c8132216b 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ShareIntentsTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/ShareIntentsTest.kt @@ -91,10 +91,10 @@ class ShareIntentsTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(mockWebServerRule) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SignInActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SignInActivityTest.kt index 6243b1e4f5..9e2afa9f52 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SignInActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/SignInActivityTest.kt @@ -45,8 +45,8 @@ class SignInActivityTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/StandardReplyWithServiceInfoAndOneFileTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/StandardReplyWithServiceInfoAndOneFileTest.kt index ba805a8581..e4f2f4ac08 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/StandardReplyWithServiceInfoAndOneFileTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/StandardReplyWithServiceInfoAndOneFileTest.kt @@ -5,8 +5,6 @@ package com.flowcrypt.email.ui.activity -import android.content.Intent -import android.os.Parcelable import android.text.TextUtils import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.replaceText @@ -88,21 +86,20 @@ class StandardReplyWithServiceInfoAndOneFileTest : BaseTest() { override val useIntents: Boolean = true override val activityScenarioRule = activityScenarioRule( - intent = Intent(getTargetContext(), CreateMessageActivity::class.java).apply { - putExtra(CreateMessageActivity.EXTRA_KEY_INCOMING_MESSAGE_INFO, incomingMsgInfo) - putExtra(CreateMessageActivity.EXTRA_KEY_MESSAGE_TYPE, MessageType.REPLY as Parcelable) - putExtra( - CreateMessageActivity.EXTRA_KEY_MESSAGE_ENCRYPTION_TYPE, - MessageEncryptionType.STANDARD as Parcelable - ) - putExtra(CreateMessageActivity.EXTRA_KEY_SERVICE_INFO, serviceInfo) - }) + intent = CreateMessageActivity.generateIntent( + getTargetContext(), + msgInfo = incomingMsgInfo, + messageType = MessageType.REPLY, + msgEncryptionType = MessageEncryptionType.STANDARD, + serviceInfo = serviceInfo + ) + ) @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/base/BaseCreateOrImportKeyActivityTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/base/BaseCreateOrImportKeyActivityTest.kt index 3a5fa3052d..5ff276afb5 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/base/BaseCreateOrImportKeyActivityTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/base/BaseCreateOrImportKeyActivityTest.kt @@ -15,8 +15,6 @@ import androidx.test.espresso.intent.Intents.intending import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtraWithKey import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.MediumTest import com.flowcrypt.email.R import com.flowcrypt.email.base.BaseTest import com.flowcrypt.email.ui.activity.CreateOrImportKeyActivity @@ -24,7 +22,6 @@ import com.flowcrypt.email.ui.activity.ImportPrivateKeyActivity import org.hamcrest.Matchers.allOf import org.junit.Assert import org.junit.Test -import org.junit.runner.RunWith /** * @author Denis Bondarenko @@ -32,8 +29,6 @@ import org.junit.runner.RunWith * Time: 10:11 AM * E-mail: DenBond7@gmail.com */ -@MediumTest -@RunWith(AndroidJUnit4::class) abstract class BaseCreateOrImportKeyActivityTest : BaseTest() { @Test diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/AddNewAccountActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/AddNewAccountActivityEnterpriseTest.kt index 7cc539a9e5..a8062113a9 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/AddNewAccountActivityEnterpriseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/AddNewAccountActivityEnterpriseTest.kt @@ -58,9 +58,9 @@ class AddNewAccountActivityEnterpriseTest : BaseSignActivityTest() { @get:Rule val ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(AddAccountToDatabaseRule()) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreateOrImportKeyActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreateOrImportKeyActivityEnterpriseTest.kt index 0d4c538da5..752621ade7 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreateOrImportKeyActivityEnterpriseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreateOrImportKeyActivityEnterpriseTest.kt @@ -46,8 +46,8 @@ class CreateOrImportKeyActivityEnterpriseTest : BaseCreateOrImportKeyActivityTes @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreatePrivateKeyActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreatePrivateKeyActivityEnterpriseTest.kt index fc3aaaf04d..8eb2fd2f4e 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreatePrivateKeyActivityEnterpriseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/CreatePrivateKeyActivityEnterpriseTest.kt @@ -86,9 +86,9 @@ class CreatePrivateKeyActivityEnterpriseTest : BasePassphraseActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(mockWebServerRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SettingsActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SettingsActivityEnterpriseTest.kt index 36742077db..3b33a36d52 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SettingsActivityEnterpriseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SettingsActivityEnterpriseTest.kt @@ -39,13 +39,13 @@ class SettingsActivityEnterpriseTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around( AddAccountToDatabaseRule( AccountDaoManager.getAccountDao("enterprise_account_no_prv_backup.json") ) ) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt index df1334e2b3..7cb4f409b1 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt @@ -110,9 +110,9 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(mockWebServerRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchForDomainTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchForDomainTest.kt index b437a3ea51..8264ef26f6 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchForDomainTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchForDomainTest.kt @@ -66,10 +66,10 @@ class CreateMessageFragmentDisallowAttesterSearchForDomainTest : BaseCreateMessa @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activeActivityRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchTest.kt index a237345d74..56de819386 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragmentDisallowAttesterSearchTest.kt @@ -74,10 +74,10 @@ class CreateMessageFragmentDisallowAttesterSearchTest : BaseCreateMessageActivit @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentEkmTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentEkmTest.kt index f7ebf85b87..5ead28ac18 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentEkmTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentEkmTest.kt @@ -82,10 +82,10 @@ class PrivateKeyDetailsFragmentEkmTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentTest.kt index 91f1c8dc81..325960fc6a 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeyDetailsFragmentTest.kt @@ -74,10 +74,10 @@ class PrivateKeyDetailsFragmentTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeysListFragmentEkmTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeysListFragmentEkmTest.kt index 7fcd79c3b1..df51db4397 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeysListFragmentEkmTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/fragment/PrivateKeysListFragmentEkmTest.kt @@ -74,10 +74,10 @@ class PrivateKeysListFragmentEkmTest : BaseTest() { @get:Rule var ruleChain: TestRule = RuleChain - .outerRule(ClearAppSettingsRule()) + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) .around(addAccountToDatabaseRule) .around(addPrivateKeyToDatabaseRule) - .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) diff --git a/FlowCrypt/src/main/AndroidManifest.xml b/FlowCrypt/src/main/AndroidManifest.xml index 371f2d51c3..7b3495a09d 100644 --- a/FlowCrypt/src/main/AndroidManifest.xml +++ b/FlowCrypt/src/main/AndroidManifest.xml @@ -95,6 +95,7 @@ android:name=".ui.activity.CreateMessageActivity" android:exported="true" android:screenOrientation="portrait"> + diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt index a906257551..070a6c7ff2 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/EmailUtil.kt @@ -673,6 +673,8 @@ class EmailUtil { MessageType.REPLY, MessageType.REPLY_ALL -> { prepareReplyMsg(context, session, outgoingMsgInfo, pubKeys, prvKeys, ringProtector) } + + else -> throw IllegalStateException("Unsupported message type") } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/OutgoingMessageInfo.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/OutgoingMessageInfo.kt index 620b8461e6..dbf9be1397 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/OutgoingMessageInfo.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/OutgoingMessageInfo.kt @@ -31,7 +31,7 @@ data class OutgoingMessageInfo constructor( val atts: List? = null, val forwardedAtts: List? = null, val encryptionType: MessageEncryptionType, - val messageType: MessageType, + @MessageType val messageType: Int, val replyToMsgEntity: MessageEntity? = null, val uid: Long = 0, val password: CharArray? = null @@ -64,7 +64,7 @@ data class OutgoingMessageInfo constructor( parcel.readValue(AttachmentInfo::class.java.classLoader) as List?, parcel.readValue(AttachmentInfo::class.java.classLoader) as List?, parcel.readParcelable(MessageEncryptionType::class.java.classLoader)!!, - parcel.readParcelable(MessageType::class.java.classLoader)!!, + parcel.readInt(), parcel.readParcelable(MessageEntity::class.java.classLoader), parcel.readLong(), parcel.createCharArray() @@ -86,7 +86,7 @@ data class OutgoingMessageInfo constructor( writeValue(atts) writeValue(forwardedAtts) writeParcelable(encryptionType, flags) - writeParcelable(messageType, flags) + writeInt(messageType) writeParcelable(replyToMsgEntity, flags) writeLong(uid) writeCharArray(password) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt index 5fe0309cd2..c5274ee6b4 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt @@ -26,7 +26,6 @@ import com.flowcrypt.email.api.retrofit.response.model.OrgRules import com.flowcrypt.email.api.retrofit.response.oauth2.MicrosoftOAuth2TokenResponse import com.flowcrypt.email.api.wkd.WkdClient import com.flowcrypt.email.extensions.kotlin.isValidEmail -import com.flowcrypt.email.extensions.kotlin.isValidLocalhostEmail import com.flowcrypt.email.extensions.org.bouncycastle.openpgp.armor import com.google.gson.GsonBuilder import com.google.gson.JsonObject @@ -140,7 +139,7 @@ class FlowcryptApiRepository : ApiRepository { } } - if (identData.isValidEmail() || identData.isValidLocalhostEmail()) { + if (identData.isValidEmail()) { val wkdResult = getResult(requestCode = requestCode) { val pgpPublicKeyRingCollection = WkdClient.lookupEmail(context, identData) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/wkd/WkdClient.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/wkd/WkdClient.kt index 031a017494..717aa4f19d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/wkd/WkdClient.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/wkd/WkdClient.kt @@ -10,7 +10,6 @@ import android.content.Context import com.flowcrypt.email.api.retrofit.ApiHelper import com.flowcrypt.email.api.retrofit.ApiService import com.flowcrypt.email.extensions.kotlin.isValidEmail -import com.flowcrypt.email.extensions.kotlin.isValidLocalhostEmail import com.flowcrypt.email.util.BetterInternetAddress import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -33,12 +32,12 @@ object WkdClient { suspend fun lookupEmail(context: Context, email: String): PGPPublicKeyRingCollection? = withContext(Dispatchers.IO) { val pgpPublicKeyRingCollection = rawLookupEmail(context, email) - val lowerCaseEmail = email.toLowerCase(Locale.ROOT) + val lowerCaseEmail = email.lowercase() val matchingKeys = pgpPublicKeyRingCollection?.keyRings?.asSequence()?.filter { for (userId in it.publicKey.userIDs) { try { val parsed = BetterInternetAddress(userId) - if (parsed.emailAddress.toLowerCase(Locale.ROOT) == lowerCaseEmail) return@filter true + if (parsed.emailAddress.lowercase() == lowerCaseEmail) return@filter true } catch (ex: Exception) { ex.printStackTrace() } @@ -55,13 +54,13 @@ object WkdClient { email: String, wkdPort: Int? = null ): PGPPublicKeyRingCollection? = withContext(Dispatchers.IO) { - if (!email.isValidEmail() && !email.isValidLocalhostEmail()) { + if (!email.isValidEmail()) { throw IllegalArgumentException("Invalid email address") } val parts = email.split('@') - val user = parts[0].toLowerCase(Locale.ROOT) - val directDomain = parts[1].toLowerCase(Locale.ROOT) + val user = parts[0].lowercase(Locale.ROOT) + val directDomain = parts[1].lowercase(Locale.ROOT) val advancedDomainPrefix = if (directDomain == "localhost") "" else "openpgpkey." val hu = ZBase32().encodeAsString(DigestUtils.sha1(user.toByteArray())) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/RecipientEntity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/RecipientEntity.kt index aeb9ac5ba5..5fb20dc040 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/RecipientEntity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/RecipientEntity.kt @@ -41,11 +41,6 @@ data class RecipientEntity( parcel.readLong() ) - //todo-denbond7 need to think about this class. - enum class Type { - TO, CC, BCC - } - override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeValue(id) parcel.writeString(email) 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 e4967f145e..f8a53d3655 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 @@ -162,7 +162,3 @@ fun String.stripTrailing(ch: Char): String { fun String.isValidEmail(): Boolean { return BetterInternetAddress.isValidEmail(this) } - -fun String.isValidLocalhostEmail(): Boolean { - return BetterInternetAddress.isValidLocalhostEmail(this) -} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ComposeMsgViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ComposeMsgViewModel.kt new file mode 100644 index 0000000000..678e144285 --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ComposeMsgViewModel.kt @@ -0,0 +1,142 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.jetpack.viewmodel + +import android.app.Application +import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys +import com.flowcrypt.email.model.MessageEncryptionType +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import java.io.InvalidObjectException +import javax.mail.Message + +/** + * @author Denis Bondarenko + * Date: 12/23/21 + * Time: 10:44 AM + * E-mail: DenBond7@gmail.com + */ +class ComposeMsgViewModel(isCandidateToEncrypt: Boolean, application: Application) : + BaseAndroidViewModel(application) { + private val messageEncryptionTypeMutableStateFlow: MutableStateFlow = + MutableStateFlow( + if (isCandidateToEncrypt) { + MessageEncryptionType.ENCRYPTED + } else { + MessageEncryptionType.STANDARD + } + ) + val messageEncryptionTypeStateFlow: StateFlow = + messageEncryptionTypeMutableStateFlow.asStateFlow() + + private val webPortalPasswordMutableStateFlow: MutableStateFlow = + MutableStateFlow("") + val webPortalPasswordStateFlow: StateFlow = + webPortalPasswordMutableStateFlow.asStateFlow() + + //session cache for recipients + private val recipientsTo = mutableMapOf() + private val recipientsCc = mutableMapOf() + private val recipientsBcc = mutableMapOf() + + private val recipientsToMutableStateFlow: MutableStateFlow> = + MutableStateFlow(emptyList()) + val recipientsToStateFlow: StateFlow> = + recipientsToMutableStateFlow.asStateFlow() + private val recipientsCcMutableStateFlow: MutableStateFlow> = + MutableStateFlow(emptyList()) + val recipientsCcStateFlow: StateFlow> = + recipientsCcMutableStateFlow.asStateFlow() + private val recipientsBccMutableStateFlow: MutableStateFlow> = + MutableStateFlow(emptyList()) + val recipientsBccStateFlow: StateFlow> = + recipientsBccMutableStateFlow.asStateFlow() + + val recipientsStateFlow = combine( + recipientsToStateFlow, + recipientsCcStateFlow, + recipientsBccStateFlow + ) { a, b, c -> + a + b + c + } + + val msgEncryptionType: MessageEncryptionType + get() = messageEncryptionTypeStateFlow.value + val recipientWithPubKeysTo: List + get() = recipientsTo.values.toList() + val recipientWithPubKeysCc: List + get() = recipientsCc.values.toList() + val recipientWithPubKeysBcc: List + get() = recipientsBcc.values.toList() + val recipientWithPubKeys: List + get() = recipientWithPubKeysTo + recipientWithPubKeysCc + recipientWithPubKeysBcc + + fun switchMessageEncryptionType(messageEncryptionType: MessageEncryptionType) { + messageEncryptionTypeMutableStateFlow.value = messageEncryptionType + } + + fun setWebPortalPassword(webPortalPassword: CharSequence = "") { + webPortalPasswordMutableStateFlow.value = webPortalPassword + } + + fun replaceRecipients(recipientType: Message.RecipientType, list: List) { + val existingRecipients = when (recipientType) { + Message.RecipientType.TO -> recipientsTo + Message.RecipientType.CC -> recipientsCc + Message.RecipientType.BCC -> recipientsBcc + else -> throw InvalidObjectException( + "unknown RecipientType: $recipientType" + ) + } + + existingRecipients.clear() + existingRecipients.putAll( + list.associateBy( + { it.recipient.email }, + { Recipient(recipientType, it) }) + ) + + notifyDataChanges(recipientType, existingRecipients) + } + + fun removeRecipient( + recipientType: Message.RecipientType, + recipientWithPubKeys: RecipientWithPubKeys + ) { + val existingRecipients = when (recipientType) { + Message.RecipientType.TO -> recipientsTo + Message.RecipientType.CC -> recipientsCc + Message.RecipientType.BCC -> recipientsBcc + else -> throw InvalidObjectException( + "unknown RecipientType: $recipientType" + ) + } + + existingRecipients.remove(recipientWithPubKeys.recipient.email) + notifyDataChanges(recipientType, existingRecipients) + } + + private fun notifyDataChanges( + recipientType: Message.RecipientType, + recipients: MutableMap + ) { + when (recipientType) { + Message.RecipientType.TO -> recipientsToMutableStateFlow + Message.RecipientType.CC -> recipientsCcMutableStateFlow + Message.RecipientType.BCC -> recipientsBccMutableStateFlow + else -> throw InvalidObjectException( + "Attempt to resolve unknown RecipientType: $recipientType" + ) + }.value = recipients.values.toList() + } + + data class Recipient( + val recipientType: Message.RecipientType, + val recipientWithPubKeys: RecipientWithPubKeys + ) +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/PasswordStrengthViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/PasswordStrengthViewModel.kt index b5cc4cdae2..9a93193840 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/PasswordStrengthViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/PasswordStrengthViewModel.kt @@ -6,15 +6,22 @@ package com.flowcrypt.email.jetpack.viewmodel import android.app.Application -import androidx.lifecycle.MutableLiveData +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.flowcrypt.email.Constants +import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.security.pgp.PgpPwd import com.flowcrypt.email.util.coroutines.runners.ControlledRunner +import com.flowcrypt.email.util.exception.IllegalTextForStrengthMeasuringException import com.nulabinc.zxcvbn.Zxcvbn +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.* /** @@ -29,14 +36,30 @@ class PasswordStrengthViewModel(application: Application) : BaseAndroidViewModel private val zxcvbn: Zxcvbn = Zxcvbn() private val controlledRunnerForZxcvbn = ControlledRunner>() - val pwdStrengthResultLiveData: MutableLiveData> = - MutableLiveData() + private val pwdStrengthResultMutableStateFlow: MutableStateFlow> = + MutableStateFlow(Result.none()) + val pwdStrengthResultStateFlow: StateFlow> = + pwdStrengthResultMutableStateFlow.asStateFlow() - fun check(passphrase: String) { + fun check(passphrase: CharSequence) { viewModelScope.launch { - pwdStrengthResultLiveData.value = Result.loading() - val measure = zxcvbn.measure(passphrase, arrayListOf(*Constants.PASSWORD_WEAK_WORDS)).guesses - pwdStrengthResultLiveData.value = controlledRunnerForZxcvbn.cancelPreviousThenRun { + val context: Context = getApplication() + if (passphrase.isEmpty()) { + pwdStrengthResultMutableStateFlow.value = Result.exception( + IllegalTextForStrengthMeasuringException(context.getString(R.string.type_text_to_start_measuring)) + ) + return@launch + } + + pwdStrengthResultMutableStateFlow.value = Result.loading() + + pwdStrengthResultMutableStateFlow.value = controlledRunnerForZxcvbn.cancelPreviousThenRun { + val measure = withContext(Dispatchers.IO) { + zxcvbn.measure( + passphrase, + arrayListOf(*Constants.PASSWORD_WEAK_WORDS) + ).guesses + } return@cancelPreviousThenRun Result.success(PgpPwd.estimateStrength(measure.toBigDecimal())) } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RecipientsViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RecipientsViewModel.kt index 2d472ea22d..844258f59d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RecipientsViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RecipientsViewModel.kt @@ -36,6 +36,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.IOException import java.util.* +import javax.mail.Message /** * This is used in the message compose/reply view when recipient public keys need to be retrieved, @@ -80,7 +81,7 @@ class RecipientsViewModel(application: Application) : AccountViewModel(applicati return roomDatabase.recipientDao().getRecipientsWithPubKeysByEmailsLD(recipientEntity.email) } - fun fetchAndUpdateInfoAboutRecipients(type: RecipientEntity.Type, emails: List) { + fun fetchAndUpdateInfoAboutRecipients(type: Message.RecipientType, emails: List) { viewModelScope.launch { if (emails.isEmpty()) { return@launch @@ -248,19 +249,19 @@ class RecipientsViewModel(application: Application) : AccountViewModel(applicati } private fun setResultForRemoteContactsLiveData( - type: RecipientEntity.Type, + type: Message.RecipientType, result: Result> ) { when (type) { - RecipientEntity.Type.TO -> { + Message.RecipientType.TO -> { recipientsToLiveData.value = result } - RecipientEntity.Type.CC -> { + Message.RecipientType.CC -> { recipientsCcLiveData.value = result } - RecipientEntity.Type.BCC -> { + Message.RecipientType.BCC -> { recipientsBccLiveData.value = result } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/model/MessageType.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/model/MessageType.kt index 3ff0c69618..2c2d5adaf7 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/model/MessageType.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/model/MessageType.kt @@ -5,8 +5,11 @@ package com.flowcrypt.email.model -import android.os.Parcel -import android.os.Parcelable +import androidx.annotation.IntDef +import com.flowcrypt.email.model.MessageType.Companion.FORWARD +import com.flowcrypt.email.model.MessageType.Companion.NEW +import com.flowcrypt.email.model.MessageType.Companion.REPLY +import com.flowcrypt.email.model.MessageType.Companion.REPLY_ALL /** * The message types. @@ -16,22 +19,13 @@ import android.os.Parcelable * Time: 12:55 * E-mail: DenBond7@gmail.com */ -enum class MessageType : Parcelable { - NEW, REPLY, REPLY_ALL, FORWARD; - - override fun describeContents(): Int { - return 0 - } - - override fun writeToParcel(dest: Parcel, flags: Int) { - dest.writeInt(ordinal) - } - +@Retention(AnnotationRetention.SOURCE) +@IntDef(NEW, REPLY, REPLY_ALL, FORWARD) +annotation class MessageType { companion object { - @JvmField - val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): MessageType = values()[source.readInt()] - override fun newArray(size: Int): Array = arrayOfNulls(size) - } + const val NEW = 0 + const val REPLY = 1 + const val REPLY_ALL = 2 + const val FORWARD = 3 } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateMessageActivity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateMessageActivity.kt index b90b79eba9..ecb15c5f8b 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateMessageActivity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateMessageActivity.kt @@ -8,15 +8,11 @@ package com.flowcrypt.email.ui.activity import android.content.Context import android.content.Intent import android.os.Bundle -import android.os.Parcelable -import android.view.Menu -import android.view.MenuItem import android.view.View -import android.widget.TextView import android.widget.Toast +import androidx.navigation.fragment.NavHostFragment import com.flowcrypt.email.R import com.flowcrypt.email.api.email.model.IncomingMessageInfo -import com.flowcrypt.email.api.email.model.OutgoingMessageInfo import com.flowcrypt.email.api.email.model.ServiceInfo import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.database.entity.AccountEntity @@ -24,14 +20,8 @@ import com.flowcrypt.email.extensions.decrementSafely import com.flowcrypt.email.extensions.incrementSafely import com.flowcrypt.email.model.MessageEncryptionType import com.flowcrypt.email.model.MessageType -import com.flowcrypt.email.service.PrepareOutgoingMessagesJobIntentService import com.flowcrypt.email.ui.activity.base.BaseBackStackSyncActivity -import com.flowcrypt.email.ui.activity.fragment.base.CreateMessageFragment import com.flowcrypt.email.ui.activity.fragment.dialog.ChoosePublicKeyDialogFragment -import com.flowcrypt.email.ui.activity.listeners.OnChangeMessageEncryptionTypeListener -import com.flowcrypt.email.ui.activity.settings.FeedbackActivity -import com.flowcrypt.email.util.GeneralUtil -import com.flowcrypt.email.util.UIUtil /** * This activity describes a logic of send encrypted or standard message. @@ -42,108 +32,16 @@ import com.flowcrypt.email.util.UIUtil * E-mail: DenBond7@gmail.com */ class CreateMessageActivity : BaseBackStackSyncActivity(), - CreateMessageFragment.OnMessageSendListener, - OnChangeMessageEncryptionTypeListener, ChoosePublicKeyDialogFragment.OnLoadKeysProgressListener { - - private var nonEncryptedHintView: View? = null - override lateinit var rootView: View - - override var msgEncryptionType = MessageEncryptionType.ENCRYPTED - private set - private var serviceInfo: ServiceInfo? = null - + ChoosePublicKeyDialogFragment.OnLoadKeysProgressListener { override val contentViewResourceId: Int get() = R.layout.activity_create_message + override val rootView: View + get() = findViewById(R.id.fragmentContainerView) override fun onCreate(savedInstanceState: Bundle?) { - if (intent != null) { - serviceInfo = intent.getParcelableExtra(EXTRA_KEY_SERVICE_INFO) - msgEncryptionType = intent.getParcelableExtra(EXTRA_KEY_MESSAGE_ENCRYPTION_TYPE) - ?: MessageEncryptionType.ENCRYPTED - } - super.onCreate(savedInstanceState) - - rootView = findViewById(R.id.createMessageFragment) - initNonEncryptedHintView() - - prepareActionBarTitle() - onMsgEncryptionTypeChanged(msgEncryptionType) - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.activity_send_message, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onPrepareOptionsMenu(menu: Menu): Boolean { - super.onPrepareOptionsMenu(menu) - val menuActionSwitchType = menu.findItem(R.id.menuActionSwitchType) - val titleRes = if (msgEncryptionType === MessageEncryptionType.STANDARD) - R.string.switch_to_secure_email - else - R.string - .switch_to_standard_email - menuActionSwitchType.setTitle(titleRes) - - if (serviceInfo?.isMsgTypeSwitchable == false) { - menu.removeItem(R.id.menuActionSwitchType) - } - - if (serviceInfo?.hasAbilityToAddNewAtt == false) { - menu.removeItem(R.id.menuActionAttachFile) - } - - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.menuActionHelp -> { - FeedbackActivity.show(this) - return true - } - - R.id.menuActionSwitchType -> { - when (msgEncryptionType) { - MessageEncryptionType.ENCRYPTED -> onMsgEncryptionTypeChanged(MessageEncryptionType.STANDARD) - - MessageEncryptionType.STANDARD -> onMsgEncryptionTypeChanged(MessageEncryptionType.ENCRYPTED) - } - return true - } - - else -> return super.onOptionsItemSelected(item) - } - } - - override fun sendMsg(outgoingMsgInfo: OutgoingMessageInfo) { - PrepareOutgoingMessagesJobIntentService.enqueueWork(this, outgoingMsgInfo) - Toast.makeText( - this, if (GeneralUtil.isConnected(this)) - R.string.sending - else - R.string.no_conn_msg_sent_later, Toast.LENGTH_SHORT - ).show() - finish() - } - - override fun onMsgEncryptionTypeChanged(messageEncryptionType: MessageEncryptionType) { - this.msgEncryptionType = messageEncryptionType - when (messageEncryptionType) { - MessageEncryptionType.ENCRYPTED -> { - appBarLayout?.setBackgroundColor(UIUtil.getColor(this, R.color.colorPrimary)) - appBarLayout?.removeView(nonEncryptedHintView) - } - - MessageEncryptionType.STANDARD -> { - appBarLayout?.setBackgroundColor(UIUtil.getColor(this, R.color.red)) - appBarLayout?.addView(nonEncryptedHintView) - } - } - - invalidateOptionsMenu() - notifyFragmentAboutChangeMsgEncryptionType(messageEncryptionType) + (supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as? NavHostFragment) + ?.navController?.setGraph(R.navigation.create_msg_graph, intent.extras) } override fun onAccountInfoRefreshed(accountEntity: AccountEntity?) { @@ -163,88 +61,27 @@ class CreateMessageActivity : BaseBackStackSyncActivity(), } } - private fun prepareActionBarTitle() { - if (supportActionBar != null) { - if (intent.hasExtra(EXTRA_KEY_MESSAGE_TYPE)) { - val msgType = intent.getParcelableExtra(EXTRA_KEY_MESSAGE_TYPE) - - msgType?.let { - when (it) { - MessageType.NEW -> supportActionBar?.setTitle(R.string.compose) - MessageType.REPLY -> supportActionBar?.setTitle(R.string.reply) - MessageType.REPLY_ALL -> supportActionBar?.setTitle(R.string.reply_all) - MessageType.FORWARD -> supportActionBar?.setTitle(R.string.forward) - } - } - } else { - if (intent.getParcelableExtra(EXTRA_KEY_INCOMING_MESSAGE_INFO) != null) { - supportActionBar!!.setTitle(R.string.reply) - } else { - supportActionBar!!.setTitle(R.string.compose) - } - } - } - } - - private fun notifyFragmentAboutChangeMsgEncryptionType(messageEncryptionType: MessageEncryptionType) { - val fragment = supportFragmentManager.findFragmentById( - R.id - .createMessageFragment - ) as CreateMessageFragment? - - fragment?.onMsgEncryptionTypeChange(messageEncryptionType) - } - - private fun initNonEncryptedHintView() { - nonEncryptedHintView = - layoutInflater.inflate(R.layout.under_toolbar_line_with_text, appBarLayout, false) - val textView = nonEncryptedHintView!!.findViewById(R.id.underToolbarTextTextView) - textView.setText(R.string.this_message_will_not_be_encrypted) - } - companion object { - - val EXTRA_KEY_MESSAGE_ENCRYPTION_TYPE = - GeneralUtil.generateUniqueExtraKey( - "EXTRA_KEY_MESSAGE_ENCRYPTION_TYPE", - CreateMessageActivity::class.java - ) - - val EXTRA_KEY_INCOMING_MESSAGE_INFO = - GeneralUtil.generateUniqueExtraKey( - "EXTRA_KEY_INCOMING_MESSAGE_INFO", - CreateMessageActivity::class.java - ) - - val EXTRA_KEY_SERVICE_INFO = - GeneralUtil.generateUniqueExtraKey( - "EXTRA_KEY_SERVICE_INFO", - CreateMessageActivity::class.java - ) - - val EXTRA_KEY_MESSAGE_TYPE = - GeneralUtil.generateUniqueExtraKey( - "EXTRA_KEY_MESSAGE_TYPE", - CreateMessageActivity::class.java - ) - fun generateIntent( - context: Context, msgInfo: IncomingMessageInfo?, + context: Context, + msgInfo: IncomingMessageInfo?, msgEncryptionType: MessageEncryptionType ): Intent { return generateIntent(context, msgInfo, MessageType.NEW, msgEncryptionType) } fun generateIntent( - context: Context?, msgInfo: IncomingMessageInfo?, messageType: MessageType?, - msgEncryptionType: MessageEncryptionType?, serviceInfo: ServiceInfo? = null + context: Context?, + msgInfo: IncomingMessageInfo?, + @MessageType messageType: Int, + msgEncryptionType: MessageEncryptionType?, + serviceInfo: ServiceInfo? = null ): Intent { - val intent = Intent(context, CreateMessageActivity::class.java) - intent.putExtra(EXTRA_KEY_INCOMING_MESSAGE_INFO, msgInfo) - intent.putExtra(EXTRA_KEY_MESSAGE_TYPE, messageType as Parcelable) - intent.putExtra(EXTRA_KEY_MESSAGE_ENCRYPTION_TYPE, msgEncryptionType as Parcelable) - intent.putExtra(EXTRA_KEY_SERVICE_INFO, serviceInfo) + intent.putExtra("incomingMessageInfo", msgInfo) + intent.putExtra("messageType", messageType) + intent.putExtra("encryptedByDefault", msgEncryptionType == MessageEncryptionType.ENCRYPTED) + intent.putExtra("serviceInfo", serviceInfo) return intent } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BasePassPhraseManagerActivity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BasePassPhraseManagerActivity.kt index c90c1c68c6..961b725776 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BasePassPhraseManagerActivity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BasePassPhraseManagerActivity.kt @@ -24,14 +24,18 @@ import androidx.core.content.ContextCompat import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat import androidx.core.widget.addTextChangedListener +import androidx.lifecycle.lifecycleScope import com.flowcrypt.email.Constants import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result +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.ui.activity.fragment.dialog.InfoDialogFragment import com.flowcrypt.email.ui.activity.fragment.dialog.WebViewInfoDialogFragment import com.flowcrypt.email.util.UIUtil +import com.flowcrypt.email.util.exception.IllegalTextForStrengthMeasuringException import com.google.android.material.snackbar.Snackbar import org.apache.commons.io.IOUtils import java.io.IOException @@ -286,21 +290,31 @@ abstract class BasePassPhraseManagerActivity : BaseBackStackActivity(), View.OnC Constants.PASSWORD_QUALITY_POOR -> getString(R.string.password_quality_poor) Constants.PASSWORD_QUALITY_WEAK -> getString(R.string.password_quality_weak) else -> word?.word - }?.toUpperCase(Locale.getDefault()) + }?.uppercase(Locale.getDefault()) } private fun initPasswordStrengthViewModel() { - passwordStrengthViewModel.pwdStrengthResultLiveData.observe(this) { - when (it.status) { - Result.Status.SUCCESS -> { - pwdStrengthResult = it.data - updateStrengthViews() - } + lifecycleScope.launchWhenStarted { + passwordStrengthViewModel.pwdStrengthResultStateFlow.collect { + when (it.status) { + Result.Status.LOADING -> { + countingIdlingResource.incrementSafely() + } + + Result.Status.SUCCESS -> { + pwdStrengthResult = it.data + updateStrengthViews() + countingIdlingResource.decrementSafely() + } - Result.Status.EXCEPTION -> { - val msg = it.exception?.message ?: it.exception?.javaClass?.simpleName - ?: getString(R.string.unknown_error) - Toast.makeText(this, msg, Toast.LENGTH_LONG).show() + Result.Status.EXCEPTION -> { + if (it.exception !is IllegalTextForStrengthMeasuringException) { + val msg = it.exception?.message ?: it.exception?.javaClass?.simpleName + ?: getString(R.string.unknown_error) + Toast.makeText(this@BasePassPhraseManagerActivity, msg, Toast.LENGTH_LONG).show() + } + countingIdlingResource.decrementSafely() + } } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CheckPassphraseStrengthFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CheckPassphraseStrengthFragment.kt index 83114267e4..ad1f4cde59 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CheckPassphraseStrengthFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CheckPassphraseStrengthFragment.kt @@ -24,12 +24,15 @@ import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat import androidx.core.widget.addTextChangedListener import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import com.flowcrypt.email.Constants import com.flowcrypt.email.NavGraphDirections import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.databinding.FragmentCheckPassphraseStrengthBinding +import com.flowcrypt.email.extensions.decrementSafely +import com.flowcrypt.email.extensions.incrementSafely import com.flowcrypt.email.extensions.navController import com.flowcrypt.email.extensions.toast import com.flowcrypt.email.jetpack.viewmodel.PasswordStrengthViewModel @@ -37,6 +40,7 @@ import com.flowcrypt.email.security.pgp.PgpPwd import com.flowcrypt.email.ui.activity.fragment.base.BaseFragment import com.flowcrypt.email.ui.notifications.SystemNotificationManager import com.flowcrypt.email.util.UIUtil +import com.flowcrypt.email.util.exception.IllegalTextForStrengthMeasuringException import com.google.android.material.snackbar.Snackbar import org.apache.commons.io.IOUtils import java.nio.charset.StandardCharsets @@ -155,20 +159,31 @@ class CheckPassphraseStrengthFragment : BaseFragment() { } private fun initPasswordStrengthViewModel() { - passwordStrengthViewModel.pwdStrengthResultLiveData.observe(viewLifecycleOwner) { - when (it.status) { - Result.Status.SUCCESS -> { - pwdStrengthResult = it.data - updateStrengthViews() - } + lifecycleScope.launchWhenStarted { + passwordStrengthViewModel.pwdStrengthResultStateFlow.collect { + when (it.status) { + Result.Status.LOADING -> { + baseActivity.countingIdlingResource.incrementSafely() + } - Result.Status.EXCEPTION -> { - toast( - it.exception?.message ?: it.exception?.javaClass?.simpleName - ?: getString(R.string.unknown_error), Toast.LENGTH_LONG - ) - } - else -> { + Result.Status.SUCCESS -> { + pwdStrengthResult = it.data + updateStrengthViews() + baseActivity.countingIdlingResource.decrementSafely() + } + + Result.Status.EXCEPTION -> { + if (it.exception !is IllegalTextForStrengthMeasuringException) { + toast( + it.exception?.message ?: it.exception?.javaClass?.simpleName + ?: getString(R.string.unknown_error), Toast.LENGTH_LONG + ) + } + baseActivity.countingIdlingResource.decrementSafely() + } + + else -> { + } } } } @@ -257,6 +272,6 @@ class CheckPassphraseStrengthFragment : BaseFragment() { Constants.PASSWORD_QUALITY_POOR -> getString(R.string.password_quality_poor) Constants.PASSWORD_QUALITY_WEAK -> getString(R.string.password_quality_weak) else -> word?.word - }?.toUpperCase(Locale.getDefault()) + }?.uppercase(Locale.getDefault()) } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt similarity index 55% rename from FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.kt rename to FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt index 6185751a4d..4d213b28f6 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt @@ -3,7 +3,7 @@ * Contributors: DenBond7 */ -package com.flowcrypt.email.ui.activity.fragment.base +package com.flowcrypt.email.ui.activity.fragment import android.annotation.SuppressLint import android.app.Activity @@ -13,7 +13,6 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.text.SpannableStringBuilder -import android.text.TextUtils import android.text.format.Formatter import android.util.Log import android.view.ContextMenu @@ -27,51 +26,63 @@ import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter -import android.widget.EditText import android.widget.FilterQueryProvider import android.widget.FrameLayout -import android.widget.ImageButton -import android.widget.LinearLayout import android.widget.ListView import android.widget.ProgressBar -import android.widget.ScrollView import android.widget.Spinner import android.widget.TextView import android.widget.Toast +import androidx.core.content.ContextCompat import androidx.core.content.FileProvider +import androidx.core.graphics.BlendModeColorFilterCompat +import androidx.core.graphics.BlendModeCompat +import androidx.core.view.isVisible +import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.viewModels +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.navArgs import com.flowcrypt.email.Constants import com.flowcrypt.email.R import com.flowcrypt.email.api.email.EmailUtil import com.flowcrypt.email.api.email.FoldersManager import com.flowcrypt.email.api.email.model.AttachmentInfo import com.flowcrypt.email.api.email.model.ExtraActionInfo -import com.flowcrypt.email.api.email.model.IncomingMessageInfo import com.flowcrypt.email.api.email.model.OutgoingMessageInfo -import com.flowcrypt.email.api.email.model.ServiceInfo import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.database.FlowCryptRoomDatabase import com.flowcrypt.email.database.entity.AccountEntity import com.flowcrypt.email.database.entity.RecipientEntity import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys +import com.flowcrypt.email.databinding.FragmentCreateMessageBinding import com.flowcrypt.email.extensions.decrementSafely +import com.flowcrypt.email.extensions.gone import com.flowcrypt.email.extensions.incrementSafely +import com.flowcrypt.email.extensions.invisible +import com.flowcrypt.email.extensions.navController import com.flowcrypt.email.extensions.org.bouncycastle.openpgp.toPgpKeyDetails import com.flowcrypt.email.extensions.showInfoDialog import com.flowcrypt.email.extensions.showKeyboard import com.flowcrypt.email.extensions.showNeedPassphraseDialog +import com.flowcrypt.email.extensions.toast +import com.flowcrypt.email.extensions.visible import com.flowcrypt.email.jetpack.viewmodel.AccountAliasesViewModel +import com.flowcrypt.email.jetpack.viewmodel.ComposeMsgViewModel import com.flowcrypt.email.jetpack.viewmodel.RecipientsViewModel import com.flowcrypt.email.model.MessageEncryptionType import com.flowcrypt.email.model.MessageType import com.flowcrypt.email.security.KeysStorageImpl -import com.flowcrypt.email.ui.activity.CreateMessageActivity +import com.flowcrypt.email.service.PrepareOutgoingMessagesJobIntentService import com.flowcrypt.email.ui.activity.ImportPublicKeyActivity import com.flowcrypt.email.ui.activity.SelectRecipientsActivity +import com.flowcrypt.email.ui.activity.fragment.base.BaseSyncFragment import com.flowcrypt.email.ui.activity.fragment.dialog.ChoosePublicKeyDialogFragment import com.flowcrypt.email.ui.activity.fragment.dialog.FixNeedPassphraseIssueDialogFragment import com.flowcrypt.email.ui.activity.fragment.dialog.NoPgpFoundDialogFragment -import com.flowcrypt.email.ui.activity.listeners.OnChangeMessageEncryptionTypeListener +import com.flowcrypt.email.ui.activity.fragment.dialog.ProvidePasswordToProtectMsgDialogFragment import com.flowcrypt.email.ui.adapter.FromAddressesAdapter import com.flowcrypt.email.ui.adapter.RecipientAdapter import com.flowcrypt.email.ui.widget.CustomChipSpanChipCreator @@ -84,7 +95,6 @@ import com.flowcrypt.email.util.UIUtil import com.flowcrypt.email.util.exception.ExceptionUtil import com.google.android.gms.common.util.CollectionUtils import com.google.android.material.snackbar.Snackbar -import com.google.android.material.textfield.TextInputLayout import com.hootsuite.nachos.NachoTextView import com.hootsuite.nachos.chip.Chip import com.hootsuite.nachos.terminator.ChipTerminatorHandler @@ -95,6 +105,7 @@ import org.pgpainless.key.OpenPgpV4Fingerprint import java.io.File import java.io.IOException import java.util.regex.Pattern +import javax.mail.Message import javax.mail.internet.InternetAddress /** @@ -105,49 +116,31 @@ import javax.mail.internet.InternetAddress * Time: 11:27 * E-mail: DenBond7@gmail.com */ -//todo-denbond7 exlude from the base package class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, AdapterView.OnItemSelectedListener, View.OnClickListener, PgpContactsNachoTextView.OnChipLongClickListener { - private lateinit var onMsgSendListener: OnMessageSendListener - private lateinit var listener: OnChangeMessageEncryptionTypeListener private lateinit var draftCacheDir: File + private val args by navArgs() private val accountAliasesViewModel: AccountAliasesViewModel by viewModels() private val recipientsViewModel: RecipientsViewModel by viewModels() + private val composeMsgViewModel: ComposeMsgViewModel by viewModels { + object : ViewModelProvider.AndroidViewModelFactory(requireActivity().application) { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return ComposeMsgViewModel(args.encryptedByDefault, requireActivity().application) as T + } + } + } - private var recipientWithPubKeysTo: MutableList? = null - private var recipientWithPubKeysCc: MutableList? = null - private var recipientWithPubKeysBcc: MutableList? = null - private val atts: MutableList? + private var binding: FragmentCreateMessageBinding? = null + private val attachments: MutableList = mutableListOf() private var folderType: FoldersManager.FolderType? = null - private var msgInfo: IncomingMessageInfo? = null - private var serviceInfo: ServiceInfo? = null - private var fromAddrs: FromAddressesAdapter? = null + private var fromAddressesAdapter: FromAddressesAdapter? = null private var cachedRecipientWithoutPubKeys: RecipientWithPubKeys? = null private var extraActionInfo: ExtraActionInfo? = null - private var messageType = MessageType.NEW - - private var layoutAtts: ViewGroup? = null - private var editTextFrom: EditText? = null - private var spinnerFrom: Spinner? = null - private var recipientsTo: PgpContactsNachoTextView? = null - private var recipientsCc: PgpContactsNachoTextView? = null - private var recipientsBcc: PgpContactsNachoTextView? = null - private var editTextEmailSubject: EditText? = null - private var editTextEmailMsg: EditText? = null - private var textInputLayoutMsg: TextInputLayout? = null - private var layoutContent: ScrollView? = null - private var progressBarTo: View? = null - private var progressBarCc: View? = null - private var progressBarBcc: View? = null - private var layoutCc: View? = null - private var layoutBcc: View? = null - private var progressBarAndButtonLayout: LinearLayout? = null - private var imageButtonAliases: ImageButton? = null - private var imageButtonAdditionalRecipientsVisibility: View? = null - private var iBShowQuotedText: View? = null + private var nonEncryptedHintView: View? = null private var isUpdateToCompleted = true private var isUpdateCcCompleted = true @@ -156,169 +149,18 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, private var isMsgSentToQueue: Boolean = false private var originalColor: Int = 0 - private val hostActivity: CreateMessageActivity? - get() { - return activity as? CreateMessageActivity - } - override val contentResourceId: Int = R.layout.fragment_create_message override val contentView: View? - get() = layoutContent - - /** - * Generate an outgoing message info from entered information by user. - * - * @return OutgoingMessageInfo Return a created OutgoingMessageInfo object which - * contains information about an outgoing message. - */ - private fun getOutgoingMsgInfo(): OutgoingMessageInfo { - var msg = editTextEmailMsg?.text.toString() - if (messageType == MessageType.REPLY || messageType == MessageType.REPLY_ALL) { - if (iBShowQuotedText?.visibility == View.VISIBLE) { - msg += EmailUtil.genReplyContent(msgInfo) - } - } - - val attachments = atts?.minus(forwardedAtts) - attachments?.forEachIndexed { index, attachmentInfo -> attachmentInfo.path = index.toString() } - - return OutgoingMessageInfo( - account = accountViewModel.activeAccountLiveData.value?.email ?: "", - subject = editTextEmailSubject?.text.toString(), - msg = msg, - toRecipients = recipientsTo?.chipValues?.map { InternetAddress(it) } ?: emptyList(), - ccRecipients = recipientsCc?.chipValues?.map { InternetAddress(it) }, - bccRecipients = recipientsBcc?.chipValues?.map { InternetAddress(it) }, - from = editTextFrom?.text.toString(), - atts = attachments, - forwardedAtts = forwardedAtts, - encryptionType = listener.msgEncryptionType, - messageType = messageType, - replyToMsgEntity = msgInfo?.msgEntity, - uid = EmailUtil.genOutboxUID(context) - ) - } - - /** - * Do a lot of checks to validate an outgoing message info. - * - * @return Boolean true if all information is correct, false otherwise. - */ - private val isDataCorrect: Boolean - get() { - recipientsTo?.chipifyAllUnterminatedTokens() - recipientsCc?.chipifyAllUnterminatedTokens() - recipientsBcc?.chipifyAllUnterminatedTokens() - if (fromAddrs?.isEnabled( - spinnerFrom?.selectedItemPosition - ?: Spinner.INVALID_POSITION - ) == false - ) { - showInfoSnackbar(recipientsTo!!, getString(R.string.no_key_available)) - return false - } - if (recipientsTo?.text?.isEmpty() == true) { - showInfoSnackbar( - recipientsTo!!, getString( - R.string.text_must_not_be_empty, - getString(R.string.prompt_recipients_to) - ) - ) - recipientsTo?.requestFocus() - return false - } - if (hasInvalidEmail(recipientsTo, recipientsCc, recipientsBcc)) { - return false - } - if (listener.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { - if (recipientsTo?.text?.isNotEmpty() == true && recipientWithPubKeysTo?.isEmpty() == true) { - fetchDetailsAboutRecipients(RecipientEntity.Type.TO) - return false - } - if (recipientsCc?.text?.isNotEmpty() == true && recipientWithPubKeysCc?.isEmpty() == true) { - fetchDetailsAboutRecipients(RecipientEntity.Type.CC) - return false - } - if (recipientsBcc?.text?.isNotEmpty() == true && recipientWithPubKeysBcc?.isEmpty() == true) { - fetchDetailsAboutRecipients(RecipientEntity.Type.BCC) - return false - } - if (hasUnusableRecipient( - recipientWithPubKeysTo, - recipientWithPubKeysCc, - recipientWithPubKeysBcc - ) - ) { - return false - } - } - if (editTextEmailSubject?.text?.isEmpty() == true) { - showInfoSnackbar( - editTextEmailSubject, getString( - R.string.text_must_not_be_empty, - getString(R.string.prompt_subject) - ) - ) - editTextEmailSubject?.requestFocus() - return false - } - if (atts?.isEmpty() == true && editTextEmailMsg?.text?.isEmpty() == true) { - showInfoSnackbar(editTextEmailMsg, getString(R.string.sending_message_must_not_be_empty)) - editTextEmailMsg?.requestFocus() - return false - } - if (atts?.isEmpty() == true || !hasExternalStorageUris(this.atts)) { - return true - } - return false - } - - - /** - * Generate a forwarded attachments list. - * - * @return The generated list. - */ - private val forwardedAtts: MutableList - get() { - val atts = mutableListOf() - - this.atts?.let { - for (att in it) { - if (att.id != null && att.isForwarded) { - atts.add(att) - } - } - } - - return atts - } - - init { - recipientWithPubKeysTo = mutableListOf() - recipientWithPubKeysCc = mutableListOf() - recipientWithPubKeysBcc = mutableListOf() - atts = ArrayList() - } + get() = binding?.scrollView override fun onAttach(context: Context) { super.onAttach(context) - if (context is OnMessageSendListener) { - this.onMsgSendListener = context - } else - throw IllegalArgumentException( - context.toString() + " must implement " + - OnMessageSendListener::class.java.simpleName - ) - - if (context is OnChangeMessageEncryptionTypeListener) { - this.listener = context - } else - throw IllegalArgumentException( - context.toString() + " must implement " + - OnChangeMessageEncryptionTypeListener::class.java.simpleName - ) + fromAddressesAdapter = FromAddressesAdapter( + context, android.R.layout.simple_list_item_1, android.R.id.text1, ArrayList() + ) + fromAddressesAdapter?.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + fromAddressesAdapter?.setUseKeysInfo(composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED) initDraftCacheDir(context) } @@ -326,31 +168,29 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) - - context?.let { - fromAddrs = FromAddressesAdapter( - it, android.R.layout.simple_list_item_1, android.R.id - .text1, ArrayList() - ) - } - fromAddrs?.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - fromAddrs?.setUseKeysInfo(listener.msgEncryptionType === MessageEncryptionType.ENCRYPTED) - + setupComposeMsgViewModel() + subscribeToSetWebPortalPassword() initExtras(activity?.intent) } + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View? { + binding = FragmentCreateMessageBinding.inflate(inflater, container, false) + return binding?.root + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initViews(view) + updateActionBarTitle() + initNonEncryptedHintView() + initViews() setupAccountAliasesViewModel() setupPrivateKeysViewModel() setupRecipientsViewModel() - } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - val isEncryptedMode = listener.msgEncryptionType === MessageEncryptionType.ENCRYPTED - if (msgInfo != null && GeneralUtil.isConnected(context) && isEncryptedMode) { + val isEncryptedMode = composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED + if (args.incomingMessageInfo != null && GeneralUtil.isConnected(context) && isEncryptedMode) { updateRecipients() } } @@ -358,12 +198,10 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onDestroy() { super.onDestroy() if (!isMsgSentToQueue) { - atts?.let { - for (att in it) { - att.uri?.let { uri -> - if (Constants.FILE_PROVIDER_AUTHORITY.equals(uri.authority, ignoreCase = true)) { - context?.contentResolver?.delete(uri, null, null) - } + for (att in attachments) { + att.uri?.let { uri -> + if (Constants.FILE_PROVIDER_AUTHORITY.equals(uri.authority, ignoreCase = true)) { + context?.contentResolver?.delete(uri, null, null) } } } @@ -373,8 +211,9 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { REQUEST_CODE_NO_PGP_FOUND_DIALOG -> when (resultCode) { - NoPgpFoundDialogFragment.RESULT_CODE_SWITCH_TO_STANDARD_EMAIL -> - listener.onMsgEncryptionTypeChanged(MessageEncryptionType.STANDARD) + NoPgpFoundDialogFragment.RESULT_CODE_SWITCH_TO_STANDARD_EMAIL -> { + composeMsgViewModel.switchMessageEncryptionType(MessageEncryptionType.STANDARD) + } NoPgpFoundDialogFragment.RESULT_CODE_IMPORT_THEIR_PUBLIC_KEY -> if (data != null) { val recipientWithPubKeys = data.getParcelableExtra( @@ -411,11 +250,27 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, ) if (recipientWithPubKeys != null) { - removeRecipientWithPubKey(recipientWithPubKeys, recipientsTo, recipientWithPubKeysTo) - removeRecipientWithPubKey(recipientWithPubKeys, recipientsCc, recipientWithPubKeysCc) - removeRecipientWithPubKey(recipientWithPubKeys, recipientsBcc, recipientWithPubKeysBcc) + removeRecipientWithPubKey( + recipientWithPubKeys, + binding?.editTextRecipientTo, + Message.RecipientType.TO + ) + removeRecipientWithPubKey( + recipientWithPubKeys, + binding?.editTextRecipientCc, + Message.RecipientType.CC + ) + removeRecipientWithPubKey( + recipientWithPubKeys, + binding?.editTextRecipientBcc, + Message.RecipientType.BCC + ) } } + + NoPgpFoundDialogFragment.RESULT_CODE_PROTECT_WITH_PASSWORD -> { + binding?.btnSetWebPortalPassword?.callOnClick() + } } REQUEST_CODE_IMPORT_PUBLIC_KEY -> when (resultCode) { @@ -437,9 +292,14 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, ) updateRecipients() - updateChips(recipientsTo, recipientWithPubKeysTo) - updateChips(recipientsCc, recipientWithPubKeysCc) - updateChips(recipientsBcc, recipientWithPubKeysBcc) + updateChips(binding?.editTextRecipientTo, + composeMsgViewModel.recipientWithPubKeysTo.map { it.recipientWithPubKeys }) + updateChips( + binding?.editTextRecipientCc, + composeMsgViewModel.recipientWithPubKeysCc.map { it.recipientWithPubKeys }) + updateChips( + binding?.editTextRecipientBcc, + composeMsgViewModel.recipientWithPubKeysBcc.map { it.recipientWithPubKeys }) Toast.makeText(context, R.string.key_successfully_copied, Toast.LENGTH_LONG).show() } @@ -462,8 +322,8 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, data.getParcelableArrayListExtra(ChoosePublicKeyDialogFragment.KEY_ATTACHMENT_INFO_LIST) ?: return val key = keyList.first() - if (atts?.none { it.name == key.name && it.encodedSize == key.encodedSize } == true) { - atts.add(key) + if (attachments.none { it.name == key.name && it.encodedSize == key.encodedSize }) { + attachments.add(key) showAtts() } } @@ -485,6 +345,25 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, inflater.inflate(R.menu.fragment_compose, menu) } + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + val menuActionSwitchType = menu.findItem(R.id.menuActionSwitchType) + val titleRes = if (composeMsgViewModel.msgEncryptionType === MessageEncryptionType.STANDARD) + R.string.switch_to_secure_email + else + R.string + .switch_to_standard_email + menuActionSwitchType.setTitle(titleRes) + + if (args.serviceInfo?.isMsgTypeSwitchable == false) { + menu.removeItem(R.id.menuActionSwitchType) + } + + if (args.serviceInfo?.hasAbilityToAddNewAtt == false) { + menu.removeItem(R.id.menuActionAttachFile) + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menuActionSend -> { @@ -492,10 +371,10 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, if (isUpdateToCompleted && isUpdateCcCompleted && isUpdateBccCompleted) { UIUtil.hideSoftInput(context, view) - if (isDataCorrect) { - if (listener.msgEncryptionType == MessageEncryptionType.ENCRYPTED) { + if (isDataCorrect()) { + if (composeMsgViewModel.msgEncryptionType == MessageEncryptionType.ENCRYPTED) { val keysStorage = KeysStorageImpl.getInstance(requireContext()) - val senderEmail = editTextFrom?.text.toString() + val senderEmail = binding?.editTextFrom?.text.toString() val keyRings = keysStorage.getPGPSecretKeyRingsByUserId(senderEmail) if (keyRings.isNotEmpty()) { val firstMatchedSecretKey = keyRings.first() @@ -534,6 +413,18 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, return true } + R.id.menuActionSwitchType -> { + when (composeMsgViewModel.msgEncryptionType) { + MessageEncryptionType.ENCRYPTED -> composeMsgViewModel.switchMessageEncryptionType( + MessageEncryptionType.STANDARD + ) + MessageEncryptionType.STANDARD -> composeMsgViewModel.switchMessageEncryptionType( + MessageEncryptionType.ENCRYPTED + ) + } + return true + } + else -> return super.onOptionsItemSelected(item) } } @@ -555,8 +446,8 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onContextItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menuDeleteQuotedText -> { - iBShowQuotedText?.visibility = View.GONE - msgInfo?.text = "" + binding?.iBShowQuotedText?.gone() + args.incomingMessageInfo?.text = "" true } @@ -567,39 +458,36 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onFocusChange(v: View, hasFocus: Boolean) { when (v.id) { R.id.editTextRecipientTo -> runUpdateActionForRecipients( - recipientWithPubKeysTo, progressBarTo, - RecipientEntity.Type.TO, hasFocus + Message.RecipientType.TO, hasFocus, (v as TextView).text.isEmpty() ) R.id.editTextRecipientCc -> runUpdateActionForRecipients( - recipientWithPubKeysCc, progressBarCc, - RecipientEntity.Type.CC, hasFocus + Message.RecipientType.CC, hasFocus, (v as TextView).text.isEmpty() ) R.id.editTextRecipientBcc -> runUpdateActionForRecipients( - recipientWithPubKeysBcc, progressBarBcc, - RecipientEntity.Type.BCC, hasFocus + Message.RecipientType.BCC, hasFocus, (v as TextView).text.isEmpty() ) R.id.editTextEmailSubject, R.id.editTextEmailMessage -> if (hasFocus) { var isExpandButtonNeeded = false - if (TextUtils.isEmpty(recipientsCc!!.text)) { - layoutCc?.visibility = View.GONE + if (binding?.editTextRecipientCc?.text?.isEmpty() == true) { + binding?.layoutCc?.gone() isExpandButtonNeeded = true } - if (TextUtils.isEmpty(recipientsBcc!!.text)) { - layoutBcc?.visibility = View.GONE + if (binding?.editTextRecipientBcc?.text?.isEmpty() == true) { + binding?.layoutBcc?.gone() isExpandButtonNeeded = true } if (isExpandButtonNeeded) { - imageButtonAdditionalRecipientsVisibility?.visibility = View.VISIBLE + binding?.imageButtonAdditionalRecipientsVisibility?.visible() val layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT ) layoutParams.gravity = Gravity.TOP or Gravity.END - progressBarAndButtonLayout?.layoutParams = layoutParams + binding?.progressBarAndButtonLayout?.layoutParams = layoutParams } } } @@ -608,13 +496,13 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { when (parent?.id) { R.id.spinnerFrom -> { - editTextFrom?.setText(parent.adapter.getItem(position) as CharSequence) - if (listener.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { + binding?.editTextFrom?.setText(parent.adapter.getItem(position) as CharSequence) + if (composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { val adapter = parent.adapter as ArrayAdapter<*> val colorGray = UIUtil.getColor(requireContext(), R.color.gray) - editTextFrom?.setTextColor(if (adapter.isEnabled(position)) originalColor else colorGray) + binding?.editTextFrom?.setTextColor(if (adapter.isEnabled(position)) originalColor else colorGray) } else { - editTextFrom?.setTextColor(originalColor) + binding?.editTextFrom?.setTextColor(originalColor) } } } @@ -626,31 +514,31 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onClick(v: View) { when (v.id) { - R.id.imageButtonAliases -> if (fromAddrs?.count != 1 && fromAddrs?.count != 0) { - spinnerFrom?.performClick() + R.id.imageButtonAliases -> if (fromAddressesAdapter?.count ?: 0 > 1) { + binding?.spinnerFrom?.performClick() } R.id.imageButtonAdditionalRecipientsVisibility -> { - layoutCc?.visibility = View.VISIBLE - layoutBcc?.visibility = View.VISIBLE + binding?.layoutCc?.visible() + binding?.layoutBcc?.visible() val layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.MATCH_PARENT ) layoutParams.gravity = Gravity.TOP or Gravity.END - progressBarAndButtonLayout?.layoutParams = layoutParams + binding?.progressBarAndButtonLayout?.layoutParams = layoutParams v.visibility = View.GONE - recipientsCc?.requestFocus() + binding?.editTextRecipientCc?.requestFocus() } R.id.iBShowQuotedText -> { - val currentCursorPosition = editTextEmailMsg?.selectionStart ?: 0 - if (editTextEmailMsg?.text?.isNotEmpty() == true) { - editTextEmailMsg?.append("\n" + EmailUtil.genReplyContent(msgInfo)) + val currentCursorPosition = binding?.editTextEmailMessage?.selectionStart ?: 0 + if (binding?.editTextEmailMessage?.text?.isNotEmpty() == true) { + binding?.editTextEmailMessage?.append("\n" + EmailUtil.genReplyContent(args.incomingMessageInfo)) } else { - editTextEmailMsg?.append(EmailUtil.genReplyContent(msgInfo)) + binding?.editTextEmailMessage?.append(EmailUtil.genReplyContent(args.incomingMessageInfo)) } - editTextEmailMsg?.setSelection(currentCursorPosition) + binding?.editTextEmailMessage?.setSelection(currentCursorPosition) v.visibility = View.GONE } } @@ -661,47 +549,53 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, override fun onAccountInfoRefreshed(accountEntity: AccountEntity?) { super.onAccountInfoRefreshed(accountEntity) accountEntity?.email?.let { email -> - if (fromAddrs?.objects?.contains(email) == false) { - fromAddrs?.add(email) + if (fromAddressesAdapter?.objects?.contains(email) == false) { + fromAddressesAdapter?.add(email) } } } - fun onMsgEncryptionTypeChange(messageEncryptionType: MessageEncryptionType?) { + private fun onMsgEncryptionTypeChange(messageEncryptionType: MessageEncryptionType?) { var emailMassageHint: String? = null if (messageEncryptionType != null) { when (messageEncryptionType) { MessageEncryptionType.ENCRYPTED -> { emailMassageHint = getString(R.string.prompt_compose_security_email) - recipientsTo?.onFocusChangeListener?.onFocusChange(recipientsTo, false) - recipientsCc?.onFocusChangeListener?.onFocusChange(recipientsCc, false) - recipientsBcc?.onFocusChangeListener?.onFocusChange(recipientsBcc, false) - fromAddrs?.setUseKeysInfo(true) + binding?.editTextRecipientTo?.onFocusChangeListener?.onFocusChange( + binding?.editTextRecipientTo, + false + ) + binding?.editTextRecipientCc?.onFocusChangeListener?.onFocusChange( + binding?.editTextRecipientCc, + false + ) + binding?.editTextRecipientBcc?.onFocusChangeListener?.onFocusChange( + binding?.editTextRecipientBcc, + false + ) + fromAddressesAdapter?.setUseKeysInfo(true) val colorGray = UIUtil.getColor(requireContext(), R.color.gray) - val selectedItemPosition = spinnerFrom?.selectedItemPosition + val selectedItemPosition = binding?.spinnerFrom?.selectedItemPosition if (selectedItemPosition != null && selectedItemPosition != AdapterView.INVALID_POSITION - && spinnerFrom?.adapter?.count ?: 0 > selectedItemPosition + && binding?.spinnerFrom?.adapter?.count ?: 0 > selectedItemPosition ) { - val isItemEnabled = fromAddrs?.isEnabled(selectedItemPosition) ?: true - editTextFrom?.setTextColor(if (isItemEnabled) originalColor else colorGray) + val isItemEnabled = fromAddressesAdapter?.isEnabled(selectedItemPosition) ?: true + binding?.editTextFrom?.setTextColor(if (isItemEnabled) originalColor else colorGray) } } MessageEncryptionType.STANDARD -> { emailMassageHint = getString(R.string.prompt_compose_standard_email) - recipientWithPubKeysTo?.clear() - recipientWithPubKeysCc?.clear() - recipientWithPubKeysBcc?.clear() isUpdateToCompleted = true isUpdateCcCompleted = true isUpdateBccCompleted = true - fromAddrs?.setUseKeysInfo(false) - editTextFrom?.setTextColor(originalColor) + fromAddressesAdapter?.setUseKeysInfo(false) + binding?.editTextFrom?.setTextColor(originalColor) } } } - textInputLayoutMsg?.hint = emailMassageHint + binding?.textInputLayoutEmailMessage?.hint = emailMassageHint } private fun attachFile() { @@ -717,26 +611,15 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, private fun initExtras(intent: Intent?) { if (intent != null) { - if (intent.hasExtra(CreateMessageActivity.EXTRA_KEY_MESSAGE_TYPE)) { - this.messageType = - intent.getSerializableExtra(CreateMessageActivity.EXTRA_KEY_MESSAGE_TYPE) as MessageType - } - - if (!TextUtils.isEmpty(intent.action) && intent.action?.startsWith("android.intent.action") == true) { + if (intent.action?.startsWith("android.intent.action") == true) { this.extraActionInfo = ExtraActionInfo.parseExtraActionInfo(requireContext(), intent) addAtts() } else { - this.serviceInfo = intent.getParcelableExtra(CreateMessageActivity.EXTRA_KEY_SERVICE_INFO) - this.msgInfo = - intent.getParcelableExtra(CreateMessageActivity.EXTRA_KEY_INCOMING_MESSAGE_INFO) - - if (msgInfo != null && msgInfo!!.localFolder != null) { - this.folderType = FoldersManager.getFolderType(msgInfo!!.localFolder) + args.incomingMessageInfo?.localFolder?.let { + this.folderType = FoldersManager.getFolderType(it) } - if (this.serviceInfo != null && this.serviceInfo!!.atts != null) { - atts?.addAll(this.serviceInfo!!.atts!!) - } + this.args.serviceInfo?.atts?.let { attachments.addAll(it) } } } } @@ -791,7 +674,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, draftAtt ) attachmentInfo.uri = uri - atts?.add(attachmentInfo) + attachments.add(attachmentInfo) } } catch (e: IOException) { e.printStackTrace() @@ -810,83 +693,78 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } private fun updateRecipients() { - recipientsTo?.chipAndTokenValues?.let { + binding?.editTextRecipientTo?.chipAndTokenValues?.let { recipientsViewModel.fetchAndUpdateInfoAboutRecipients( - RecipientEntity.Type.TO, + Message.RecipientType.TO, it ) } - if (layoutCc?.visibility == View.VISIBLE) { - recipientsCc?.chipAndTokenValues?.let { + if (binding?.layoutCc?.isVisible == true) { + binding?.editTextRecipientCc?.chipAndTokenValues?.let { recipientsViewModel.fetchAndUpdateInfoAboutRecipients( - RecipientEntity.Type.CC, + Message.RecipientType.CC, it ) } } else { - recipientsCc?.setText(null as CharSequence?) - recipientWithPubKeysCc?.clear() + binding?.editTextRecipientCc?.setText(null as CharSequence?) + composeMsgViewModel.replaceRecipients(Message.RecipientType.CC, emptyList()) } - if (layoutBcc?.visibility == View.VISIBLE) { - recipientsBcc?.chipAndTokenValues?.let { + if (binding?.layoutBcc?.isVisible == true) { + binding?.editTextRecipientBcc?.chipAndTokenValues?.let { recipientsViewModel.fetchAndUpdateInfoAboutRecipients( - RecipientEntity.Type.BCC, + Message.RecipientType.BCC, it ) } } else { - recipientsBcc?.setText(null as CharSequence?) - recipientWithPubKeysBcc?.clear() + binding?.editTextRecipientBcc?.setText(null as CharSequence?) + composeMsgViewModel.replaceRecipients(Message.RecipientType.BCC, emptyList()) } } /** * Run an action to update information about some [RecipientWithPubKeys]s. * - * @param recipients Old [RecipientWithPubKeys]s - * @param progressBar A [ProgressBar] which is showing an action progress. * @param type A type of recipients * @param hasFocus A value which indicates the view focus. * @return A modified recipients list. */ private fun runUpdateActionForRecipients( - recipients: MutableList?, - progressBar: View?, - type: RecipientEntity.Type, - hasFocus: Boolean - ): List? { - if (listener.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { - if (hasFocus) { - recipients?.clear() - } else { - if (isAdded) { - fetchDetailsAboutRecipients(type) - } + type: Message.RecipientType, + hasFocus: Boolean, + isEmpty: Boolean + ) { + if (composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { + if (!hasFocus && isAdded) { + fetchDetailsAboutRecipients(type) } } - return recipients + if (isEmpty) { + composeMsgViewModel.replaceRecipients(type, emptyList()) + } } - private fun fetchDetailsAboutRecipients(type: RecipientEntity.Type) { + private fun fetchDetailsAboutRecipients(type: Message.RecipientType) { when (type) { - RecipientEntity.Type.TO -> { - recipientsTo?.chipAndTokenValues?.let { - recipientsViewModel.fetchAndUpdateInfoAboutRecipients(RecipientEntity.Type.TO, it) + Message.RecipientType.TO -> { + binding?.editTextRecipientTo?.chipAndTokenValues?.let { + recipientsViewModel.fetchAndUpdateInfoAboutRecipients(Message.RecipientType.TO, it) } } - RecipientEntity.Type.CC -> { - recipientsCc?.chipAndTokenValues?.let { - recipientsViewModel.fetchAndUpdateInfoAboutRecipients(RecipientEntity.Type.CC, it) + Message.RecipientType.CC -> { + binding?.editTextRecipientCc?.chipAndTokenValues?.let { + recipientsViewModel.fetchAndUpdateInfoAboutRecipients(Message.RecipientType.CC, it) } } - RecipientEntity.Type.BCC -> { - recipientsBcc?.chipAndTokenValues?.let { - recipientsViewModel.fetchAndUpdateInfoAboutRecipients(RecipientEntity.Type.BCC, it) + Message.RecipientType.BCC -> { + binding?.editTextRecipientBcc?.chipAndTokenValues?.let { + recipientsViewModel.fetchAndUpdateInfoAboutRecipients(Message.RecipientType.BCC, it) } } } @@ -899,12 +777,12 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, * @param aliases A list of Gmail aliases. */ private fun prepareAliasForReplyIfNeeded(aliases: List) { - val messageEncryptionType = listener.msgEncryptionType + val messageEncryptionType = composeMsgViewModel.msgEncryptionType val toAddresses: List? = if (folderType === FoldersManager.FolderType.SENT) { - msgInfo?.getFrom() + args.incomingMessageInfo?.getFrom() } else { - msgInfo?.getTo() + args.incomingMessageInfo?.getTo() } if (!CollectionUtils.isEmpty(toAddresses)) { @@ -914,7 +792,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, for (alias in aliases) { if (alias.equals(toAddress.address, ignoreCase = true)) { firstFoundedAlias = if (messageEncryptionType === MessageEncryptionType.ENCRYPTED - && fromAddrs?.hasPrvKey(alias) == true + && fromAddressesAdapter?.hasPrvKey(alias) == true ) { alias } else { @@ -929,9 +807,10 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } if (firstFoundedAlias != null) { - val position = fromAddrs?.getPosition(firstFoundedAlias) ?: Spinner.INVALID_POSITION + val position = + fromAddressesAdapter?.getPosition(firstFoundedAlias) ?: Spinner.INVALID_POSITION if (position != Spinner.INVALID_POSITION) { - spinnerFrom?.setSelection(position) + binding?.spinnerFrom?.setSelection(position) } } } @@ -940,16 +819,17 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, private fun showFirstMatchedAliasWithPrvKey(aliases: List) { var firstFoundedAliasWithPrvKey: String? = null for (alias in aliases) { - if (fromAddrs?.hasPrvKey(alias) == true) { + if (fromAddressesAdapter?.hasPrvKey(alias) == true) { firstFoundedAliasWithPrvKey = alias break } } if (firstFoundedAliasWithPrvKey != null) { - val position = fromAddrs?.getPosition(firstFoundedAliasWithPrvKey) ?: Spinner.INVALID_POSITION + val position = + fromAddressesAdapter?.getPosition(firstFoundedAliasWithPrvKey) ?: Spinner.INVALID_POSITION if (position != Spinner.INVALID_POSITION) { - spinnerFrom?.setSelection(position) + binding?.spinnerFrom?.setSelection(position) } } } @@ -957,30 +837,34 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, /** * Check that all recipients are usable. */ - private fun hasUnusableRecipient(vararg list: List?): Boolean { - for (sublist in list) { - sublist?.let { - for (recipientWithPubKeys in it) { - if (!recipientWithPubKeys.hasAtLeastOnePubKey()) { - showNoPgpFoundDialog(recipientWithPubKeys, true) - return true - } + private fun hasUnusableRecipient(): Boolean { + for (recipient in composeMsgViewModel.recipientWithPubKeys) { + val recipientWithPubKeys = recipient.recipientWithPubKeys + if (!recipientWithPubKeys.hasAtLeastOnePubKey()) { + return if (isPasswordProtectedFunctionalityEnabled()) { + if (composeMsgViewModel.webPortalPasswordStateFlow.value.isEmpty()) { + showNoPgpFoundDialog(recipientWithPubKeys) + true + } else continue + } else { + showNoPgpFoundDialog(recipientWithPubKeys) + true + } + } - if (!recipientWithPubKeys.hasNotExpiredPubKey()) { - showInfoDialog(dialogMsg = getString(R.string.warning_one_of_recipients_has_expired_pub_key)) - return true - } + if (!recipientWithPubKeys.hasNotExpiredPubKey()) { + showInfoDialog(dialogMsg = getString(R.string.warning_one_of_recipients_has_expired_pub_key)) + return true + } - if (!recipientWithPubKeys.hasNotRevokedPubKey()) { - showInfoDialog(dialogMsg = getString(R.string.warning_one_of_recipients_has_revoked_pub_key)) - return true - } + if (!recipientWithPubKeys.hasNotRevokedPubKey()) { + showInfoDialog(dialogMsg = getString(R.string.warning_one_of_recipients_has_revoked_pub_key)) + return true + } - if (!recipientWithPubKeys.hasUsablePubKey()) { - showInfoDialog(dialogMsg = getString(R.string.warning_one_of_recipients_has_not_usable_pub_key)) - return true - } - } + if (!recipientWithPubKeys.hasUsablePubKey()) { + showInfoDialog(dialogMsg = getString(R.string.warning_one_of_recipients_has_not_usable_pub_key)) + return true } } @@ -1058,11 +942,10 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, * * @param recipientWithPubKeys The [RecipientWithPubKeys] which will be removed. * @param pgpContactsNachoTextView The [NachoTextView] which contains the delete candidate. - * @param list The list which contains the delete candidate. */ private fun removeRecipientWithPubKey( recipientWithPubKeys: RecipientWithPubKeys, pgpContactsNachoTextView: PgpContactsNachoTextView?, - list: MutableList? + recipientType: Message.RecipientType ) { val chipTokenizer = pgpContactsNachoTextView?.chipTokenizer pgpContactsNachoTextView?.allChips?.let { @@ -1077,72 +960,48 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } } - val iterator = list?.iterator() - while (iterator?.hasNext() == true) { - val next = iterator.next() - if (next.recipient.email.equals(recipientWithPubKeys.recipient.email, ignoreCase = true)) { - iterator.remove() - } - } + composeMsgViewModel.removeRecipient(recipientType, recipientWithPubKeys) } /** * Init fragment views - * - * @param view The root fragment view. */ - private fun initViews(view: View) { - layoutContent = view.findViewById(R.id.scrollView) - layoutAtts = view.findViewById(R.id.layoutAtts) - layoutCc = view.findViewById(R.id.layoutCc) - layoutBcc = view.findViewById(R.id.layoutBcc) - progressBarAndButtonLayout = view.findViewById(R.id.progressBarAndButtonLayout) - - recipientsTo = view.findViewById(R.id.editTextRecipientTo) - recipientsCc = view.findViewById(R.id.editTextRecipientCc) - recipientsBcc = view.findViewById(R.id.editTextRecipientBcc) + private fun initViews() { + initChipsView(binding?.editTextRecipientTo) + initChipsView(binding?.editTextRecipientCc) + initChipsView(binding?.editTextRecipientBcc) - initChipsView(recipientsTo) - initChipsView(recipientsCc) - initChipsView(recipientsBcc) + binding?.spinnerFrom?.onItemSelectedListener = this + binding?.spinnerFrom?.adapter = fromAddressesAdapter - spinnerFrom = view.findViewById(R.id.spinnerFrom) - spinnerFrom?.onItemSelectedListener = this - spinnerFrom?.adapter = fromAddrs + originalColor = requireNotNull(binding?.editTextFrom?.currentTextColor) - editTextFrom = view.findViewById(R.id.editTextFrom) - originalColor = editTextFrom!!.currentTextColor + binding?.imageButtonAliases?.setOnClickListener(this) - imageButtonAliases = view.findViewById(R.id.imageButtonAliases) - imageButtonAliases?.setOnClickListener(this) + binding?.imageButtonAdditionalRecipientsVisibility?.setOnClickListener(this) - imageButtonAdditionalRecipientsVisibility = - view.findViewById(R.id.imageButtonAdditionalRecipientsVisibility) - imageButtonAdditionalRecipientsVisibility?.setOnClickListener(this) - - editTextEmailSubject = view.findViewById(R.id.editTextEmailSubject) - editTextEmailSubject?.onFocusChangeListener = this - editTextEmailMsg = view.findViewById(R.id.editTextEmailMessage) - editTextEmailMsg?.onFocusChangeListener = this - textInputLayoutMsg = view.findViewById(R.id.textInputLayoutEmailMessage) - iBShowQuotedText = view.findViewById(R.id.iBShowQuotedText) - iBShowQuotedText?.setOnClickListener(this) - - progressBarTo = view.findViewById(R.id.progressBarTo) - progressBarCc = view.findViewById(R.id.progressBarCc) - progressBarBcc = view.findViewById(R.id.progressBarBcc) + binding?.editTextEmailSubject?.onFocusChangeListener = this + binding?.editTextEmailMessage?.onFocusChangeListener = this + binding?.iBShowQuotedText?.setOnClickListener(this) + binding?.btnSetWebPortalPassword?.setOnClickListener { + navController?.navigate( + CreateMessageFragmentDirections + .actionCreateMessageFragmentToProvidePasswordToProtectMsgDialogFragment( + composeMsgViewModel.webPortalPasswordStateFlow.value.toString() + ) + ) + } } private fun showContent() { UIUtil.exchangeViewVisibility(false, progressView, contentView) - if ((msgInfo != null || extraActionInfo != null) && !isIncomingMsgInfoUsed) { + if ((args.incomingMessageInfo != null || extraActionInfo != null) && !isIncomingMsgInfoUsed) { this.isIncomingMsgInfoUsed = true updateViews() } showAtts() - hostActivity?.countingIdlingResource?.decrementSafely("showContent") } /** @@ -1150,87 +1009,99 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, * screen. */ private fun updateViews() { - onMsgEncryptionTypeChange(listener.msgEncryptionType) + onMsgEncryptionTypeChange(composeMsgViewModel.msgEncryptionType) if (extraActionInfo != null) { updateViewsFromExtraActionInfo() } else { - if (msgInfo != null) { + if (args.incomingMessageInfo != null) { updateViewsFromIncomingMsgInfo() - recipientsTo?.chipifyAllUnterminatedTokens() - recipientsCc?.chipifyAllUnterminatedTokens() - editTextEmailSubject?.setText(prepareReplySubject(msgInfo?.getSubject() ?: "")) + binding?.editTextRecipientTo?.chipifyAllUnterminatedTokens() + binding?.editTextRecipientCc?.chipifyAllUnterminatedTokens() + binding?.editTextEmailSubject?.setText( + prepareReplySubject( + args.incomingMessageInfo?.getSubject() ?: "" + ) + ) } - if (serviceInfo != null) { + if (args.serviceInfo != null) { updateViewsFromServiceInfo() } } } private fun updateViewsFromExtraActionInfo() { - setupPgpFromExtraActionInfo(recipientsTo, extraActionInfo!!.toAddresses.toTypedArray()) - setupPgpFromExtraActionInfo(recipientsCc, extraActionInfo!!.ccAddresses.toTypedArray()) - setupPgpFromExtraActionInfo(recipientsBcc, extraActionInfo!!.bccAddresses.toTypedArray()) + setupPgpFromExtraActionInfo( + binding?.editTextRecipientTo, + extraActionInfo?.toAddresses?.toTypedArray() + ) + setupPgpFromExtraActionInfo( + binding?.editTextRecipientCc, + extraActionInfo?.ccAddresses?.toTypedArray() + ) + setupPgpFromExtraActionInfo( + binding?.editTextRecipientBcc, + extraActionInfo?.bccAddresses?.toTypedArray() + ) - editTextEmailSubject?.setText(extraActionInfo!!.subject) - editTextEmailMsg?.setText(extraActionInfo!!.body) + binding?.editTextEmailSubject?.setText(extraActionInfo?.subject) + binding?.editTextEmailMessage?.setText(extraActionInfo?.body) - if (recipientsTo?.text?.isEmpty() == true) { - recipientsTo?.requestFocus() + if (binding?.editTextRecipientTo?.text?.isEmpty() == true) { + binding?.editTextRecipientTo?.requestFocus() return } - if (editTextEmailSubject?.text?.isEmpty() == true) { - editTextEmailSubject?.requestFocus() + if (binding?.editTextEmailSubject?.text?.isEmpty() == true) { + binding?.editTextEmailSubject?.requestFocus() return } - editTextEmailMsg?.requestFocus() - editTextEmailMsg?.showKeyboard() + binding?.editTextEmailMessage?.requestFocus() + binding?.editTextEmailMessage?.showKeyboard() } private fun updateViewsFromServiceInfo() { - recipientsTo?.isFocusable = serviceInfo!!.isToFieldEditable - recipientsTo?.isFocusableInTouchMode = serviceInfo!!.isToFieldEditable - //todo-denbond7 Need to add a similar option for recipientsCc and recipientsBcc + binding?.editTextRecipientTo?.isFocusable = args.serviceInfo?.isToFieldEditable ?: false + binding?.editTextRecipientTo?.isFocusableInTouchMode = + args.serviceInfo?.isToFieldEditable ?: false + //todo-denbond7 Need to add a similar option for editTextRecipientCc and editTextRecipientBcc - editTextEmailSubject?.isFocusable = serviceInfo?.isSubjectEditable ?: false - editTextEmailSubject?.isFocusableInTouchMode = serviceInfo?.isSubjectEditable ?: false + binding?.editTextEmailSubject?.isFocusable = args.serviceInfo?.isSubjectEditable ?: false + binding?.editTextEmailSubject?.isFocusableInTouchMode = + args.serviceInfo?.isSubjectEditable ?: false - editTextEmailMsg?.isFocusable = serviceInfo?.isMsgEditable ?: false - editTextEmailMsg?.isFocusableInTouchMode = serviceInfo?.isMsgEditable ?: false + binding?.editTextEmailMessage?.isFocusable = args.serviceInfo?.isMsgEditable ?: false + binding?.editTextEmailMessage?.isFocusableInTouchMode = args.serviceInfo?.isMsgEditable ?: false - if (serviceInfo?.systemMsg?.isNotEmpty() == true) { - editTextEmailMsg?.setText(serviceInfo?.systemMsg) + if (args.serviceInfo?.systemMsg?.isNotEmpty() == true) { + binding?.editTextEmailMessage?.setText(args.serviceInfo?.systemMsg) } } private fun updateViewsFromIncomingMsgInfo() { - iBShowQuotedText?.visibility = View.VISIBLE - iBShowQuotedText?.let { registerForContextMenu(it) } + binding?.iBShowQuotedText?.visible() + binding?.iBShowQuotedText?.let { registerForContextMenu(it) } - when (messageType) { + when (args.messageType) { MessageType.REPLY -> updateViewsIfReplyMode() MessageType.REPLY_ALL -> updateViewsIfReplyAllMode() MessageType.FORWARD -> updateViewsIfFwdMode() - - else -> { - } } updateRecipients() } private fun updateViewsIfFwdMode() { - val originalMsgInfo = msgInfo ?: return + val originalMsgInfo = args.incomingMessageInfo ?: return if (!CollectionUtils.isEmpty(originalMsgInfo.atts)) { for (att in originalMsgInfo.atts!!) { if (hasAbilityToAddAtt(att)) { - atts?.add(att) + attachments.add(att) } else { showInfoSnackbar( requireView(), getString( @@ -1243,7 +1114,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } } - editTextEmailMsg?.setText( + binding?.editTextEmailMessage?.setText( getString( R.string.forward_template, originalMsgInfo.getFrom().first().address ?: "", @@ -1254,38 +1125,39 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, ) if (!CollectionUtils.isEmpty(originalMsgInfo.getCc())) { - editTextEmailMsg?.append("Cc: ") - editTextEmailMsg?.append(prepareRecipientsLineForForwarding(originalMsgInfo.getCc())) - editTextEmailMsg?.append("\n\n") + binding?.editTextEmailMessage?.append("Cc: ") + binding?.editTextEmailMessage?.append(prepareRecipientsLineForForwarding(originalMsgInfo.getCc())) + binding?.editTextEmailMessage?.append("\n\n") } - editTextEmailMsg?.append("\n\n" + originalMsgInfo.text) + binding?.editTextEmailMessage?.append("\n\n" + originalMsgInfo.text) } private fun updateViewsIfReplyAllMode() { when (folderType) { FoldersManager.FolderType.SENT, FoldersManager.FolderType.OUTBOX -> { - recipientsTo?.setText(prepareRecipients(msgInfo?.getTo())) + binding?.editTextRecipientTo?.setText(prepareRecipients(args.incomingMessageInfo?.getTo())) - if (msgInfo?.getCc()?.isNotEmpty() == true) { - layoutCc?.visibility = View.VISIBLE - recipientsCc?.append(prepareRecipients(msgInfo?.getCc())) + if (args.incomingMessageInfo?.getCc()?.isNotEmpty() == true) { + binding?.layoutCc?.visibility = View.VISIBLE + binding?.editTextRecipientCc?.append(prepareRecipients(args.incomingMessageInfo?.getCc())) } } else -> { - val toRecipients = if (msgInfo?.getReplyToWithoutOwnerAddress().isNullOrEmpty()) { - msgInfo?.getTo() ?: emptyList() - } else { - msgInfo?.getReplyToWithoutOwnerAddress() ?: emptyList() - } + val toRecipients = + if (args.incomingMessageInfo?.getReplyToWithoutOwnerAddress().isNullOrEmpty()) { + args.incomingMessageInfo?.getTo() ?: emptyList() + } else { + args.incomingMessageInfo?.getReplyToWithoutOwnerAddress() ?: emptyList() + } - recipientsTo?.setText(prepareRecipients(toRecipients)) + binding?.editTextRecipientTo?.setText(prepareRecipients(toRecipients)) val ccSet = HashSet() - if (msgInfo?.getTo()?.isNotEmpty() == true) { - for (address in msgInfo?.getTo() ?: emptyList()) { + if (args.incomingMessageInfo?.getTo()?.isNotEmpty() == true) { + for (address in args.incomingMessageInfo?.getTo() ?: emptyList()) { if (!account?.email.equals(address.address, ignoreCase = true)) { ccSet.add(address) } @@ -1306,8 +1178,8 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, ccSet.removeAll(toRecipients.toSet()) - if (msgInfo?.getCc()?.isNotEmpty() == true) { - for (address in msgInfo?.getCc() ?: emptyList()) { + if (args.incomingMessageInfo?.getCc()?.isNotEmpty() == true) { + for (address in args.incomingMessageInfo?.getCc() ?: emptyList()) { if (!account?.email.equals(address.address, ignoreCase = true)) { ccSet.add(address) } @@ -1315,42 +1187,46 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } //here we remove the owner address - val fromAddress = msgInfo?.msgEntity?.email + val fromAddress = args.incomingMessageInfo?.msgEntity?.email val finalCcSet = ccSet.filter { fromAddress?.equals(it.address, true) != true } if (finalCcSet.isNotEmpty()) { - layoutCc?.visibility = View.VISIBLE + binding?.layoutCc?.visible() val ccRecipients = prepareRecipients(finalCcSet) - recipientsCc?.append(ccRecipients) + binding?.editTextRecipientCc?.append(ccRecipients) } } } - if (recipientsTo?.text?.isNotEmpty() == true || recipientsCc?.text?.isNotEmpty() == true) { - editTextEmailMsg?.requestFocus() - editTextEmailMsg?.showKeyboard() + if (binding?.editTextRecipientTo?.text?.isNotEmpty() == true + || binding?.editTextRecipientCc?.text?.isNotEmpty() == true + ) { + binding?.editTextEmailMessage?.requestFocus() + binding?.editTextEmailMessage?.showKeyboard() } } private fun updateViewsIfReplyMode() { when (folderType) { FoldersManager.FolderType.SENT, - FoldersManager.FolderType.OUTBOX -> recipientsTo?.setText(prepareRecipients(msgInfo?.getTo())) + FoldersManager.FolderType.OUTBOX -> { + binding?.editTextRecipientTo?.setText(prepareRecipients(args.incomingMessageInfo?.getTo())) + } - else -> recipientsTo?.setText( + else -> binding?.editTextRecipientTo?.setText( prepareRecipients( - if (msgInfo?.getReplyToWithoutOwnerAddress().isNullOrEmpty()) { - msgInfo?.getTo() + if (args.incomingMessageInfo?.getReplyToWithoutOwnerAddress().isNullOrEmpty()) { + args.incomingMessageInfo?.getTo() } else { - msgInfo?.getReplyToWithoutOwnerAddress() + args.incomingMessageInfo?.getReplyToWithoutOwnerAddress() } ) ) } - if (recipientsTo?.text?.isNotEmpty() == true) { - editTextEmailMsg?.requestFocus() - editTextEmailMsg?.showKeyboard() + if (binding?.editTextRecipientTo?.text?.isNotEmpty() == true) { + binding?.editTextEmailMessage?.requestFocus() + binding?.editTextEmailMessage?.showKeyboard() } } @@ -1387,7 +1263,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } private fun prepareReplySubject(subject: String): String { - val prefix = when (messageType) { + val prefix = when (args.messageType) { MessageType.REPLY, MessageType.REPLY_ALL -> "Re" MessageType.FORWARD -> "Fwd" else -> return subject @@ -1467,7 +1343,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, * @return true if the attachment can be added, otherwise false. */ private fun hasAbilityToAddAtt(newAttInfo: AttachmentInfo?): Boolean { - val existedAttsSize = (atts?.map { it.encodedSize.toInt() }?.sum() ?: 0) + val existedAttsSize = (attachments.map { it.encodedSize.toInt() }.sum()) val newAttSize = newAttInfo?.encodedSize?.toInt() ?: 0 return (existedAttsSize + newAttSize) < Constants.MAX_TOTAL_ATTACHMENT_SIZE_IN_BYTES } @@ -1476,14 +1352,14 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, /** * Show a dialog where we can select different actions. * - * @param recipient The [RecipientWithPubKeys] which will be used when we select the remove action. - * @param isRemoveActionEnabled true if we want to show the remove action, false otherwise. + * @param recipient The [RecipientWithPubKeys] which will be used when we select the remove action. */ - private fun showNoPgpFoundDialog( - recipient: RecipientWithPubKeys, - isRemoveActionEnabled: Boolean - ) { - val dialogFragment = NoPgpFoundDialogFragment.newInstance(recipient, isRemoveActionEnabled) + private fun showNoPgpFoundDialog(recipient: RecipientWithPubKeys) { + val dialogFragment = NoPgpFoundDialogFragment.newInstance( + RecipientWithPubKeys = recipient, + isRemoveActionEnabled = true, + isProtectingWithPasswordEnabled = isPasswordProtectedFunctionalityEnabled() + ) dialogFragment.setTargetFragment(this, REQUEST_CODE_NO_PGP_FOUND_DIALOG) dialogFragment.show(parentFragmentManager, NoPgpFoundDialogFragment::class.java.simpleName) } @@ -1493,18 +1369,26 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, */ private fun sendMsg() { dismissCurrentSnackBar() - onMsgSendListener.sendMsg(getOutgoingMsgInfo()) + val outgoingMessageInfo = getOutgoingMsgInfo() + PrepareOutgoingMessagesJobIntentService.enqueueWork(requireContext(), outgoingMessageInfo) + toast( + if (GeneralUtil.isConnected(requireContext())) + R.string.sending + else + R.string.no_conn_msg_sent_later + ) + activity?.finish() } /** * Show attachments which were added. */ private fun showAtts() { - if (atts?.isNotEmpty() == true) { - layoutAtts?.removeAllViews() + if (attachments.isNotEmpty()) { + binding?.layoutAtts?.removeAllViews() val layoutInflater = LayoutInflater.from(context) - for (att in atts) { - val rootView = layoutInflater.inflate(R.layout.attachment_item, layoutAtts, false) + for (att in attachments) { + val rootView = layoutInflater.inflate(R.layout.attachment_item, binding?.layoutAtts, false) val textViewAttName = rootView.findViewById(R.id.textViewAttachmentName) textViewAttName.text = att.name @@ -1524,8 +1408,8 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, val imageButtonClearAtt = rootView.findViewById(R.id.imageButtonClearAtt) imageButtonClearAtt.visibility = View.VISIBLE imageButtonClearAtt.setOnClickListener { - atts.remove(att) - layoutAtts?.removeView(rootView) + attachments.remove(att) + binding?.layoutAtts?.removeView(rootView) //Remove a temp file which was created by our app val uri = att.uri @@ -1540,10 +1424,10 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } else { imageButtonDownloadAtt.visibility = View.INVISIBLE } - layoutAtts?.addView(rootView) + binding?.layoutAtts?.addView(rootView) } } else { - layoutAtts?.removeAllViews() + binding?.layoutAtts?.removeAllViews() } } @@ -1562,7 +1446,6 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, private fun setupAccountAliasesViewModel() { accountAliasesViewModel.fetchUpdates(viewLifecycleOwner) - hostActivity?.countingIdlingResource?.incrementSafely() accountAliasesViewModel.accountAliasesLiveData.observe(viewLifecycleOwner, { val aliases = ArrayList() accountAliasesViewModel.activeAccountLiveData.value?.let { accountEntity -> @@ -1573,33 +1456,33 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, aliases.add(accountAlias.sendAsEmail) } - fromAddrs?.clear() - fromAddrs?.addAll(aliases) + fromAddressesAdapter?.clear() + fromAddressesAdapter?.addAll(aliases) updateFromAddressAdapter(KeysStorageImpl.getInstance(requireContext()).getPGPSecretKeyRings()) - if (msgInfo != null) { + if (args.incomingMessageInfo != null) { prepareAliasForReplyIfNeeded(aliases) - } else if (listener.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { + } else if (composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { showFirstMatchedAliasWithPrvKey(aliases) } - if (serviceInfo != null) { - serviceInfo?.let { serviceInfo -> + if (args.serviceInfo != null) { + args.serviceInfo?.let { serviceInfo -> if (serviceInfo.isFromFieldEditable) { - imageButtonAliases?.visibility = View.VISIBLE + binding?.imageButtonAliases?.visibility = View.VISIBLE } else { - imageButtonAliases?.visibility = View.INVISIBLE + binding?.imageButtonAliases?.visibility = View.INVISIBLE } } } else { - fromAddrs?.count?.let { count: Int -> + fromAddressesAdapter?.count?.let { count: Int -> if (count in 0..1) { - if (imageButtonAliases?.visibility == View.VISIBLE) { - imageButtonAliases?.visibility = View.INVISIBLE + if (binding?.imageButtonAliases?.visibility == View.VISIBLE) { + binding?.imageButtonAliases?.visibility = View.INVISIBLE } } else { - imageButtonAliases?.visibility = View.VISIBLE + binding?.imageButtonAliases?.visibility = View.VISIBLE } } } @@ -1618,7 +1501,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, .flatten() .map { mimeAddress -> mimeAddress.address } - fromAddrs?.let { adapter -> + fromAddressesAdapter?.let { adapter -> for (email in adapter.objects) { adapter.updateKeyAvailability(email, setOfUsers.contains(email)) } @@ -1626,100 +1509,67 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } private fun setupRecipientsViewModel() { - handleUpdatingToRecipients() - handleUpdatingCcRecipients() - handleUpdatingBccRecipients() - } - - private fun handleUpdatingToRecipients() { - recipientsViewModel.recipientsToLiveData.observe(viewLifecycleOwner, { - when (it.status) { - Result.Status.LOADING -> { - hostActivity?.countingIdlingResource?.incrementSafely() - recipientWithPubKeysTo?.clear() - progressBarTo?.visibility = View.VISIBLE - isUpdateToCompleted = false - } - - Result.Status.SUCCESS -> { - isUpdateToCompleted = true - progressBarTo?.visibility = View.INVISIBLE + handleUpdatingRecipients( + recipientsViewModel.recipientsToLiveData, + Message.RecipientType.TO, + binding?.progressBarTo, + binding?.editTextRecipientTo + ) { + isUpdateToCompleted = it + } - recipientWithPubKeysTo = it.data?.toMutableList() - if (recipientWithPubKeysTo?.isNotEmpty() == true) { - updateChips(recipientsTo, recipientWithPubKeysTo) - } - hostActivity?.countingIdlingResource?.decrementSafely() - } + handleUpdatingRecipients( + recipientsViewModel.recipientsCcLiveData, + Message.RecipientType.CC, + binding?.progressBarCc, + binding?.editTextRecipientCc + ) { + isUpdateCcCompleted = it + } - Result.Status.ERROR, Result.Status.EXCEPTION -> { - isUpdateToCompleted = true - progressBarTo?.visibility = View.INVISIBLE - showInfoSnackbar(view, it.exception?.message ?: getString(R.string.unknown_error)) - hostActivity?.countingIdlingResource?.decrementSafely() - } - } - }) + handleUpdatingRecipients( + recipientsViewModel.recipientsBccLiveData, + Message.RecipientType.BCC, + binding?.progressBarBcc, + binding?.editTextRecipientBcc + ) { + isUpdateBccCompleted = it + } } - private fun handleUpdatingCcRecipients() { - recipientsViewModel.recipientsCcLiveData.observe(viewLifecycleOwner, { + private fun handleUpdatingRecipients( + liveData: LiveData>>, + recipientType: Message.RecipientType, + progressBar: ProgressBar?, + nachoTextView: PgpContactsNachoTextView?, + updateState: (state: Boolean) -> Unit + ) { + liveData.observe(viewLifecycleOwner, { when (it.status) { Result.Status.LOADING -> { - hostActivity?.countingIdlingResource?.incrementSafely() - recipientWithPubKeysCc?.clear() - progressBarCc?.visibility = View.VISIBLE - isUpdateCcCompleted = false + updateState.invoke(false) + baseActivity.countingIdlingResource.incrementSafely() + progressBar?.visible() } Result.Status.SUCCESS -> { - isUpdateCcCompleted = true - progressBarCc?.visibility = View.INVISIBLE - recipientWithPubKeysCc = it.data?.toMutableList() - - if (recipientWithPubKeysCc?.isNotEmpty() == true) { - updateChips(recipientsCc, recipientWithPubKeysCc) + updateState.invoke(true) + progressBar?.invisible() + it.data?.let { list -> + composeMsgViewModel.replaceRecipients(recipientType, list) + updateChips(nachoTextView, list) } - hostActivity?.countingIdlingResource?.decrementSafely() + baseActivity.countingIdlingResource.decrementSafely() } Result.Status.ERROR, Result.Status.EXCEPTION -> { - isUpdateCcCompleted = true - progressBarCc?.visibility = View.INVISIBLE + updateState.invoke(true) + progressBar?.invisible() showInfoSnackbar(view, it.exception?.message ?: getString(R.string.unknown_error)) - hostActivity?.countingIdlingResource?.decrementSafely() - } - } - }) - } - - private fun handleUpdatingBccRecipients() { - recipientsViewModel.recipientsBccLiveData.observe(viewLifecycleOwner, { - when (it.status) { - Result.Status.LOADING -> { - hostActivity?.countingIdlingResource?.incrementSafely() - recipientWithPubKeysBcc?.clear() - progressBarBcc?.visibility = View.VISIBLE - isUpdateBccCompleted = false + baseActivity.countingIdlingResource.decrementSafely() } - Result.Status.SUCCESS -> { - isUpdateBccCompleted = true - progressBarBcc?.visibility = View.INVISIBLE - recipientWithPubKeysBcc = it.data?.toMutableList() - - if (recipientWithPubKeysBcc?.isNotEmpty() == true) { - updateChips(recipientsBcc, recipientWithPubKeysBcc) - } - hostActivity?.countingIdlingResource?.decrementSafely() - } - - Result.Status.ERROR, Result.Status.EXCEPTION -> { - isUpdateBccCompleted = true - progressBarBcc?.visibility = View.INVISIBLE - showInfoSnackbar(view, it.exception?.message ?: getString(R.string.unknown_error)) - hostActivity?.countingIdlingResource?.decrementSafely() - } + Result.Status.NONE -> {} } }) } @@ -1751,7 +1601,7 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, showInfoSnackbar(view, getString(R.string.can_not_attach_this_file), Snackbar.LENGTH_LONG) return } - attachmentInfo?.let { atts?.add(it) } + attachmentInfo?.let { attachments.add(it) } showAtts() } else { showInfoSnackbar( @@ -1767,11 +1617,224 @@ class CreateMessageFragment : BaseSyncFragment(), View.OnFocusChangeListener, } } + private fun setupComposeMsgViewModel() { + lifecycleScope.launchWhenStarted { + composeMsgViewModel.messageEncryptionTypeStateFlow.collect { + when (it) { + MessageEncryptionType.ENCRYPTED -> { + appBarLayout?.setBackgroundColor( + UIUtil.getColor(requireContext(), R.color.colorPrimary) + ) + appBarLayout?.removeView(nonEncryptedHintView) + } + + MessageEncryptionType.STANDARD -> { + appBarLayout?.setBackgroundColor(UIUtil.getColor(requireContext(), R.color.red)) + appBarLayout?.addView(nonEncryptedHintView) + } + } + + activity?.invalidateOptionsMenu() + onMsgEncryptionTypeChange(it) + } + } + + lifecycleScope.launchWhenStarted { + composeMsgViewModel.recipientsStateFlow.collect { recipients -> + if (isPasswordProtectedFunctionalityEnabled()) { + val hasRecipientsWithoutPgp = + recipients.any { recipient -> !recipient.recipientWithPubKeys.hasAtLeastOnePubKey() } + if (hasRecipientsWithoutPgp) { + if (binding?.btnSetWebPortalPassword?.visibility == View.GONE) { + composeMsgViewModel.setWebPortalPassword() + } + binding?.btnSetWebPortalPassword?.visible() + } else { + binding?.btnSetWebPortalPassword?.gone() + } + } + } + } + + lifecycleScope.launchWhenStarted { + composeMsgViewModel.webPortalPasswordStateFlow.collect { webPortalPassword -> + if (isPasswordProtectedFunctionalityEnabled()) { + binding?.btnSetWebPortalPassword?.apply { + if (webPortalPassword.isEmpty()) { + setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_password_not_protected_white_24, + 0, + 0, + 0 + ) + setText(R.string.tap_to_protect_with_web_portal_password) + background?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat( + ContextCompat.getColor(context, R.color.orange), BlendModeCompat.MODULATE + ) + } else { + setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_password_protected_white_24, + 0, + 0, + 0 + ) + setText(R.string.web_portal_password_added) + background?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat( + ContextCompat.getColor(context, R.color.colorPrimary), BlendModeCompat.MODULATE + ) + } + } + } + } + } + } + + private fun initNonEncryptedHintView() { + nonEncryptedHintView = + layoutInflater.inflate(R.layout.under_toolbar_line_with_text, appBarLayout, false) + val textView = nonEncryptedHintView?.findViewById(R.id.underToolbarTextTextView) + textView?.setText(R.string.this_message_will_not_be_encrypted) + } + + private fun updateActionBarTitle() { + when (args.messageType) { + MessageType.NEW -> supportActionBar?.setTitle(R.string.compose) + MessageType.REPLY -> supportActionBar?.setTitle(R.string.reply) + MessageType.REPLY_ALL -> supportActionBar?.setTitle(R.string.reply_all) + MessageType.FORWARD -> supportActionBar?.setTitle(R.string.forward) + } + } + /** - * This interface will be used when we send a message. + * Do a lot of checks to validate an outgoing message info. + * + * @return true if all information is correct, false otherwise. */ - interface OnMessageSendListener { - fun sendMsg(outgoingMsgInfo: OutgoingMessageInfo) + private fun isDataCorrect(): Boolean { + binding?.editTextRecipientTo?.chipifyAllUnterminatedTokens() + binding?.editTextRecipientCc?.chipifyAllUnterminatedTokens() + binding?.editTextRecipientBcc?.chipifyAllUnterminatedTokens() + if (fromAddressesAdapter?.isEnabled( + binding?.spinnerFrom?.selectedItemPosition ?: Spinner.INVALID_POSITION + ) == false + ) { + showInfoSnackbar(binding?.editTextRecipientTo, getString(R.string.no_key_available)) + return false + } + if (binding?.editTextRecipientTo?.text?.isEmpty() == true) { + showInfoSnackbar( + binding?.editTextRecipientTo, getString( + R.string.text_must_not_be_empty, + getString(R.string.prompt_recipients_to) + ) + ) + binding?.editTextRecipientTo?.requestFocus() + return false + } + if (hasInvalidEmail( + binding?.editTextRecipientTo, + binding?.editTextRecipientCc, + binding?.editTextRecipientBcc + ) + ) { + return false + } + if (composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { + if (binding?.editTextRecipientTo?.text?.isNotEmpty() == true + && composeMsgViewModel.recipientWithPubKeysTo.isEmpty() + ) { + fetchDetailsAboutRecipients(Message.RecipientType.TO) + return false + } + if (binding?.editTextRecipientCc?.text?.isNotEmpty() == true + && composeMsgViewModel.recipientWithPubKeysCc.isEmpty() + ) { + fetchDetailsAboutRecipients(Message.RecipientType.CC) + return false + } + if (binding?.editTextRecipientBcc?.text?.isNotEmpty() == true + && composeMsgViewModel.recipientWithPubKeysBcc.isEmpty() + ) { + fetchDetailsAboutRecipients(Message.RecipientType.BCC) + return false + } + if (hasUnusableRecipient()) { + return false + } + } + if (binding?.editTextEmailSubject?.text?.isEmpty() == true) { + showInfoSnackbar( + binding?.editTextEmailSubject, getString( + R.string.text_must_not_be_empty, + getString(R.string.prompt_subject) + ) + ) + binding?.editTextEmailSubject?.requestFocus() + return false + } + if (attachments.isEmpty() && binding?.editTextEmailMessage?.text?.isEmpty() == true) { + showInfoSnackbar( + binding?.editTextEmailMessage, + getString(R.string.sending_message_must_not_be_empty) + ) + binding?.editTextEmailMessage?.requestFocus() + return false + } + if (attachments.isEmpty() || !hasExternalStorageUris(this.attachments)) { + return true + } + return false + } + + private fun getOutgoingMsgInfo(): OutgoingMessageInfo { + var msg = binding?.editTextEmailMessage?.text.toString() + if (args.messageType == MessageType.REPLY || args.messageType == MessageType.REPLY_ALL) { + if (binding?.iBShowQuotedText?.visibility == View.VISIBLE) { + msg += EmailUtil.genReplyContent(args.incomingMessageInfo) + } + } + + val attachments = attachments.minus(getForwardedAttachments().toSet()) + attachments.forEachIndexed { index, attachmentInfo -> attachmentInfo.path = index.toString() } + + return OutgoingMessageInfo( + account = accountViewModel.activeAccountLiveData.value?.email ?: "", + subject = binding?.editTextEmailSubject?.text.toString(), + msg = msg, + toRecipients = binding?.editTextRecipientTo?.chipValues?.map { InternetAddress(it) } + ?: emptyList(), + ccRecipients = binding?.editTextRecipientCc?.chipValues?.map { InternetAddress(it) }, + bccRecipients = binding?.editTextRecipientBcc?.chipValues?.map { InternetAddress(it) }, + from = binding?.editTextFrom?.text.toString(), + atts = attachments, + forwardedAtts = getForwardedAttachments(), + encryptionType = composeMsgViewModel.msgEncryptionType, + messageType = args.messageType, + replyToMsgEntity = args.incomingMessageInfo?.msgEntity, + uid = EmailUtil.genOutboxUID(context), + password = if (isPasswordProtectedFunctionalityEnabled()) { + composeMsgViewModel.webPortalPasswordStateFlow.value.toString().toCharArray() + } else null + ) + } + + private fun isPasswordProtectedFunctionalityEnabled(): Boolean { + val account = accountViewModel.activeAccountLiveData.value?.email ?: return false + return EmailUtil.getDomain(account) in listOf("flowcrypt.com", "flowcrypt.test") + } + + private fun getForwardedAttachments(): List { + return attachments.filter { it.id != null && it.isForwarded } + } + + private fun subscribeToSetWebPortalPassword() { + setFragmentResultListener( + ProvidePasswordToProtectMsgDialogFragment.REQUEST_KEY_PASSWORD + ) { _, bundle -> + val password = + bundle.getCharSequence(ProvidePasswordToProtectMsgDialogFragment.KEY_PASSWORD) ?: "" + composeMsgViewModel.setWebPortalPassword(password) + } } companion object { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BaseFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BaseFragment.kt index 81d23681f1..aaec4a2294 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BaseFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/BaseFragment.kt @@ -26,6 +26,7 @@ import com.flowcrypt.email.model.results.LoaderResult import com.flowcrypt.email.ui.activity.base.BaseActivity import com.flowcrypt.email.ui.notifications.ErrorNotificationManager import com.flowcrypt.email.util.UIUtil +import com.google.android.material.appbar.AppBarLayout import com.google.android.material.snackbar.Snackbar /** @@ -69,6 +70,13 @@ abstract class BaseFragment : Fragment() { } else null + val appBarLayout: AppBarLayout? + get() = if (activity is BaseActivity) { + (activity as BaseActivity).appBarLayout + } else { + activity?.findViewById(R.id.appBarLayout) + } + val baseActivity: BaseActivity get() = activity as BaseActivity diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/NoPgpFoundDialogFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/NoPgpFoundDialogFragment.kt index fcecf1f313..b6e140de68 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/NoPgpFoundDialogFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/NoPgpFoundDialogFragment.kt @@ -30,15 +30,29 @@ class NoPgpFoundDialogFragment : BaseDialogFragment(), DialogInterface.OnClickLi private var recipientWithPubKeys: RecipientWithPubKeys? = null private var dialogItems: MutableList = mutableListOf() private var isRemoveActionEnabled: Boolean = false + private var isProtectingWithPasswordEnabled: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) this.recipientWithPubKeys = arguments?.getParcelable(EXTRA_KEY_PGP_CONTACT) this.isRemoveActionEnabled = arguments?.getBoolean(EXTRA_KEY_IS_REMOVE_ACTION_ENABLED) ?: false + this.isProtectingWithPasswordEnabled = arguments?.getBoolean( + EXTRA_KEY_IS_PROTECTING_WITH_PASSWORD_ENABLED + ) ?: false dialogItems = ArrayList() + if (isProtectingWithPasswordEnabled) { + dialogItems.add( + DialogItem( + iconResourceId = R.drawable.ic_password_protected_gray_48, + title = getString(R.string.protect_with_password), + id = RESULT_CODE_PROTECT_WITH_PASSWORD + ) + ) + } + dialogItems.add( DialogItem( iconResourceId = R.mipmap.ic_switch, @@ -99,10 +113,11 @@ class NoPgpFoundDialogFragment : BaseDialogFragment(), DialogInterface.OnClickLi } companion object { - const val RESULT_CODE_SWITCH_TO_STANDARD_EMAIL = 10 - const val RESULT_CODE_IMPORT_THEIR_PUBLIC_KEY = 11 - const val RESULT_CODE_COPY_FROM_OTHER_CONTACT = 12 - const val RESULT_CODE_REMOVE_CONTACT = 13 + const val RESULT_CODE_PROTECT_WITH_PASSWORD = 10 + const val RESULT_CODE_SWITCH_TO_STANDARD_EMAIL = 11 + const val RESULT_CODE_IMPORT_THEIR_PUBLIC_KEY = 12 + const val RESULT_CODE_COPY_FROM_OTHER_CONTACT = 13 + const val RESULT_CODE_REMOVE_CONTACT = 14 val EXTRA_KEY_PGP_CONTACT = GeneralUtil.generateUniqueExtraKey( @@ -116,13 +131,24 @@ class NoPgpFoundDialogFragment : BaseDialogFragment(), DialogInterface.OnClickLi NoPgpFoundDialogFragment::class.java ) + private val EXTRA_KEY_IS_PROTECTING_WITH_PASSWORD_ENABLED = + GeneralUtil.generateUniqueExtraKey( + "EXTRA_KEY_IS_PROTECTING_WITH_PASSWORD_ENABLED", + NoPgpFoundDialogFragment::class.java + ) + fun newInstance( RecipientWithPubKeys: RecipientWithPubKeys, - isRemoveActionEnabled: Boolean + isRemoveActionEnabled: Boolean, + isProtectingWithPasswordEnabled: Boolean = false ): NoPgpFoundDialogFragment { val args = Bundle() args.putParcelable(EXTRA_KEY_PGP_CONTACT, RecipientWithPubKeys) args.putBoolean(EXTRA_KEY_IS_REMOVE_ACTION_ENABLED, isRemoveActionEnabled) + args.putBoolean( + EXTRA_KEY_IS_PROTECTING_WITH_PASSWORD_ENABLED, + isProtectingWithPasswordEnabled + ) val noPgpFoundDialogFragment = NoPgpFoundDialogFragment() noPgpFoundDialogFragment.arguments = args return noPgpFoundDialogFragment diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ProvidePasswordToProtectMsgDialogFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ProvidePasswordToProtectMsgDialogFragment.kt new file mode 100644 index 0000000000..bbe3ddd5fd --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/dialog/ProvidePasswordToProtectMsgDialogFragment.kt @@ -0,0 +1,78 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.ui.activity.fragment.dialog + +import android.app.AlertDialog +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import androidx.core.os.bundleOf +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.setFragmentResult +import androidx.navigation.fragment.navArgs +import com.flowcrypt.email.R +import com.flowcrypt.email.databinding.FragmentProvidePasswordToProtectMsgBinding +import com.flowcrypt.email.extensions.hideKeyboard +import com.flowcrypt.email.extensions.navController +import com.flowcrypt.email.extensions.toast + +class ProvidePasswordToProtectMsgDialogFragment : BaseDialogFragment() { + private var binding: FragmentProvidePasswordToProtectMsgBinding? = null + private val args by navArgs() + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(activity) + binding = FragmentProvidePasswordToProtectMsgBinding.inflate( + LayoutInflater.from(requireContext()), + if ((view != null) and (view is ViewGroup)) view as ViewGroup? else null, + false + ) + + binding?.eTPassphrase?.addTextChangedListener { editable -> + //check typed password will be added a bit later(soon) + } + + binding?.eTPassphrase?.setOnEditorActionListener { v, actionId, _ -> + return@setOnEditorActionListener when (actionId) { + EditorInfo.IME_ACTION_DONE -> { + checkAndMoveOn() + v.hideKeyboard() + true + } + else -> false + } + } + + binding?.btSetPassphrase?.setOnClickListener { + checkAndMoveOn() + } + + binding?.eTPassphrase?.setText(args.defaultPassword) + + builder.setView(binding?.root) + builder.setTitle(null) + return builder.create() + } + + private fun checkAndMoveOn() { + if (binding?.eTPassphrase?.text?.isEmpty() == true) { + toast(getString(R.string.password_cannot_be_empty)) + } else { + navController?.navigateUp() + setFragmentResult( + REQUEST_KEY_PASSWORD, + bundleOf(KEY_PASSWORD to (binding?.eTPassphrase?.text ?: "")) + ) + } + } + + companion object { + const val REQUEST_KEY_PASSWORD = "REQUEST_KEY_PASSWORD" + const val KEY_PASSWORD = "KEY_PASSWORD" + } +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/listeners/OnChangeMessageEncryptionTypeListener.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/listeners/OnChangeMessageEncryptionTypeListener.kt deleted file mode 100644 index 0061a90a44..0000000000 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/listeners/OnChangeMessageEncryptionTypeListener.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 - */ - -package com.flowcrypt.email.ui.activity.listeners - -import com.flowcrypt.email.model.MessageEncryptionType - -/** - * This interface can be used when need to notify if [MessageEncryptionType] was changed. - * - * @author Denis Bondarenko - * Date: 28.07.2017 - * Time: 15:39 - * E-mail: DenBond7@gmail.com - */ -interface OnChangeMessageEncryptionTypeListener { - - val msgEncryptionType: MessageEncryptionType - - /** - * Handle a switch of the message encryption type. - * - * @param messageEncryptionType The new message encryption type. - */ - fun onMsgEncryptionTypeChanged(messageEncryptionType: MessageEncryptionType) -} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/BetterEmailAddress.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/BetterEmailAddress.kt index 3414c4f31d..fdab28c14d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/BetterEmailAddress.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/BetterEmailAddress.kt @@ -15,7 +15,7 @@ class BetterInternetAddress(str: String, verifySpecialCharacters: Boolean = true init { val personalNameWithEmailMatch = validPersonalNameWithEmailRegex.find(str) - val emailMatch = str.matches(validEmailRegex) || str.matches(validLocalhostEmailRegex) + val emailMatch = str.matches(validEmailRegex) when { personalNameWithEmailMatch != null -> { val group = personalNameWithEmailMatch.groupValues @@ -48,14 +48,15 @@ class BetterInternetAddress(str: String, verifySpecialCharacters: Boolean = true "(?:[${ALPHANUM}!#\$%&'*+/=?^_`{|}~-]+(?:\\.[${ALPHANUM}!#\$%&'*+/=?^" + "_`{|}~-]+)*|\"(?:[\\x01-\\x08" + "\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f" + - "])*\")@(?:(?:[${ALPHANUM}](?:[${ALPHANUM}-]*[${ALPHANUM}])?\\.)+[${ALPHANUM}](?:[" + + "])*\")@((?:(?:[${ALPHANUM}](?:[${ALPHANUM}-]*[${ALPHANUM}])?\\.)+[${ALPHANUM}](?:[" + "${ALPHANUM}-]*[${ALPHANUM}])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:" + "25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[${ALPHANUM}-]*[${ALPHANUM}]:(?:[\\x01-\\x08\\x0b" + - "\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])" + "\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])" + + "|localhost)" private const val VALID_PERSONAL_NAME_WITH_EMAIL = "([$ALPHANUM\\p{Punct}\\p{Space}]*)<($VALID_EMAIL)>" - private val validEmailRegex = VALID_EMAIL.toRegex() + private val validEmailRegex = VALID_EMAIL.toRegex(RegexOption.IGNORE_CASE) private val validPersonalNameWithEmailRegex = VALID_PERSONAL_NAME_WITH_EMAIL.toRegex() // if these appear in the display-name they must be double quoted @@ -63,7 +64,6 @@ class BetterInternetAddress(str: String, verifySpecialCharacters: Boolean = true // double quotes at ends only private val doubleQuotedTextRegex = "\"[^\"]*\"".toRegex() - private val validLocalhostEmailRegex = Regex("([a-zA-z])([a-zA-z0-9])+@localhost") private const val maxLocalPartLength = 64 fun isValidEmail(email: String): Boolean { @@ -71,11 +71,6 @@ class BetterInternetAddress(str: String, verifySpecialCharacters: Boolean = true return email.indexOf('@') < maxLocalPartLength } - fun isValidLocalhostEmail(email: String): Boolean { - if (validLocalhostEmailRegex.matchEntire(email) == null) return false - return email.indexOf('@') < maxLocalPartLength - } - @Suppress("unused") fun areValidEmails(emails: Iterable): Boolean { return emails.all { it.isValidEmail() } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/IllegalTextForStrengthMeasuringException.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/IllegalTextForStrengthMeasuringException.kt new file mode 100644 index 0000000000..cfd5171cbc --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/IllegalTextForStrengthMeasuringException.kt @@ -0,0 +1,8 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.util.exception + +class IllegalTextForStrengthMeasuringException(message: String) : IllegalArgumentException(message) diff --git a/FlowCrypt/src/main/res/drawable/ic_password_not_protected_white_24.xml b/FlowCrypt/src/main/res/drawable/ic_password_not_protected_white_24.xml new file mode 100644 index 0000000000..cc125c9a6e --- /dev/null +++ b/FlowCrypt/src/main/res/drawable/ic_password_not_protected_white_24.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/FlowCrypt/src/main/res/drawable/ic_password_protected_gray_48.xml b/FlowCrypt/src/main/res/drawable/ic_password_protected_gray_48.xml new file mode 100644 index 0000000000..44e3c58c65 --- /dev/null +++ b/FlowCrypt/src/main/res/drawable/ic_password_protected_gray_48.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/FlowCrypt/src/main/res/drawable/ic_password_protected_white_24.xml b/FlowCrypt/src/main/res/drawable/ic_password_protected_white_24.xml new file mode 100644 index 0000000000..6eb3f3a844 --- /dev/null +++ b/FlowCrypt/src/main/res/drawable/ic_password_protected_white_24.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/FlowCrypt/src/main/res/layout/activity_create_message.xml b/FlowCrypt/src/main/res/layout/activity_create_message.xml index 1732b0b99c..f55dc7a1a0 100644 --- a/FlowCrypt/src/main/res/layout/activity_create_message.xml +++ b/FlowCrypt/src/main/res/layout/activity_create_message.xml @@ -4,20 +4,19 @@ --> - - + app:defaultNavHost="true" + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> diff --git a/FlowCrypt/src/main/res/layout/fragment_create_message.xml b/FlowCrypt/src/main/res/layout/fragment_create_message.xml index 3c1d1e04dc..d05227fceb 100644 --- a/FlowCrypt/src/main/res/layout/fragment_create_message.xml +++ b/FlowCrypt/src/main/res/layout/fragment_create_message.xml @@ -3,7 +3,7 @@ ~ Contributors: DenBond7 --> - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> +