diff --git a/.editorconfig b/.editorconfig
index 56b592ba81558793917eb79a97a57be312f1008d..f76d5403c0a3219f6682970b7748427677cdd0d3 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -5,6 +5,7 @@ charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
+end_of_line = lf
[*.{kt,kts}]
ij_kotlin_imports_layout = *,^
diff --git a/.gitattributes b/.gitattributes
index 411c07777552a74b61b3523fa0b6b82a1bc03a6b..a00ea4be8664105271e98e34039af693a1eccbdf 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,5 @@
-* text=auto eol=lf
+* text=auto
-*.bat text eol=crlf
+*.bat eol=crlf
+*.eml eol=crlf
*.jar binary
diff --git a/.github/ci-gradle.properties b/.github/ci-gradle.properties
new file mode 100644
index 0000000000000000000000000000000000000000..7de8afc3ae2075890f6383f8a2b57fd17fa1e0f7
--- /dev/null
+++ b/.github/ci-gradle.properties
@@ -0,0 +1,6 @@
+org.gradle.daemon=false
+org.gradle.parallel=true
+org.gradle.workers.max=2
+
+kotlin.incremental=false
+kotlin.compiler.execution.strategy=in-process
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 6c060792b2e9bc4f7d85a2a12b1a20f1ee6a2ad3..8ace88da189860d6a7e532675576eb2339bb1f28 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -16,16 +16,34 @@ jobs:
validation:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v1
build:
runs-on: ubuntu-latest
+ timeout-minutes: 90
+
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
+
+ - name: Copy CI gradle.properties
+ run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
+
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
+
- uses: gradle/gradle-build-action@v2
- - run: ./gradlew assembleDebug detekt spotlessCheck testsOnCi
+
+ - name: Quality - Spotless
+ run: ./gradlew spotlessCheck
+
+ - name: Quality - Detekt
+ run: ./gradlew detekt
+
+ - name: Build
+ run: ./gradlew assembleDebug
+
+ - name: Test
+ run: ./gradlew testsOnCi
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index f9f570c0c3b16ca01c5b754ba8302ea4a81ce268..becf6b92d693e744a5c56cfa557c445819073aa7 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -16,7 +16,7 @@ jobs:
security-events: write
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
diff --git a/.github/workflows/gradle-cache.yml b/.github/workflows/gradle-cache.yml
index 0e4b6f3d72691bcf1a25afa65a46eea4ba840673..0f2d4c23346fc6a9c41c05d6e1b86337addd45c6 100644
--- a/.github/workflows/gradle-cache.yml
+++ b/.github/workflows/gradle-cache.yml
@@ -18,7 +18,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: temurin
diff --git a/app-feature-preview/build.gradle.kts b/app-feature-preview/build.gradle.kts
index b601ae760fa3656afc643a2863541d97cb336112..53b5803c206ace93c4d8b2f2112a4c954c4272a2 100644
--- a/app-feature-preview/build.gradle.kts
+++ b/app-feature-preview/build.gradle.kts
@@ -70,6 +70,9 @@ dependencies {
implementation(projects.feature.onboarding)
implementation(projects.feature.account.setup)
+ implementation(projects.feature.account.edit)
+
+ implementation(libs.appauth)
implementation(libs.okhttp)
implementation(libs.timber)
}
diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt
index 23dd89e99409e66099f3d76c8f6647f2aae6268c..a2b003825c389b172ce2c1a58bd7954414ec6b3c 100644
--- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt
+++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/FeatureModule.kt
@@ -1,23 +1,36 @@
package app.k9mail.feature.preview
import app.k9mail.core.common.oauth.OAuthConfigurationFactory
+import app.k9mail.feature.account.common.AccountCommonExternalContract
+import app.k9mail.feature.account.edit.AccountEditExternalContract
+import app.k9mail.feature.account.edit.featureAccountEditModule
import app.k9mail.feature.account.setup.AccountSetupExternalContract
import app.k9mail.feature.account.setup.featureAccountSetupModule
-import app.k9mail.feature.preview.account.AccountCreator
import app.k9mail.feature.preview.account.AccountOwnerNameProvider
+import app.k9mail.feature.preview.account.InMemoryAccountStore
import app.k9mail.feature.preview.auth.AndroidKeyStoreDirectoryProvider
import app.k9mail.feature.preview.auth.AppOAuthConfigurationFactory
import app.k9mail.feature.preview.auth.DefaultTrustedSocketFactory
+import app.k9mail.feature.preview.backend.RealOAuth2TokenProviderFactory
+import com.fsck.k9.mail.oauth.OAuth2TokenProviderFactory
import com.fsck.k9.mail.ssl.KeyStoreDirectoryProvider
import com.fsck.k9.mail.ssl.LocalKeyStore
import com.fsck.k9.mail.ssl.TrustManagerFactory
import com.fsck.k9.mail.ssl.TrustedSocketFactory
import org.koin.core.module.Module
+import org.koin.dsl.binds
import org.koin.dsl.module
val accountModule: Module = module {
+ single { InMemoryAccountStore() }
+ .binds(
+ arrayOf(
+ AccountCommonExternalContract.AccountStateLoader::class,
+ AccountSetupExternalContract.AccountCreator::class,
+ AccountEditExternalContract.AccountUpdater::class,
+ ),
+ )
factory ");
- html.append(filename);
+ html.append(TextUtils.htmlEncode(filename));
html.append(" message.eml ");
}
diff --git a/app/core/src/main/java/com/fsck/k9/message/html/DisplayHtml.kt b/app/core/src/main/java/com/fsck/k9/message/html/DisplayHtml.kt
index 9034f4d2ca067758ab197322a84819eca78c74e2..3e45d5e4fddc84386819bf6efe01ce7b4c64b8fb 100644
--- a/app/core/src/main/java/com/fsck/k9/message/html/DisplayHtml.kt
+++ b/app/core/src/main/java/com/fsck/k9/message/html/DisplayHtml.kt
@@ -25,8 +25,9 @@ class DisplayHtml(private val settings: HtmlSettings) : HtmlHeadProvider {
private fun cssStyleTheme(): String {
return if (settings.useDarkMode) {
+ // TODO: Don't hardcode these values. Inject them via HtmlSettings.
" "
} else {
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt
index 10b4718193ffec92385d3d18ed9f87fc8ca0c66a..f8439633d5f9e2aa5d9963eb1cef52b1aa1e954d 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationIds.kt
@@ -60,7 +60,8 @@ internal object NotificationIds {
}
private fun getBaseNotificationId(account: Account): Int {
- return 1 /* skip notification ID 0 */ + NUMBER_OF_GENERAL_NOTIFICATIONS +
+ /* skip notification ID 0 */
+ return 1 + NUMBER_OF_GENERAL_NOTIFICATIONS +
account.accountNumber * NUMBER_OF_NOTIFICATIONS_PER_ACCOUNT
}
}
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
index 1aa80b3a590537511aaf36c772e787649d18f009..dcff73e46ea512071ce1071cb92ad20d22634796 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
@@ -243,6 +243,13 @@ public class Settings {
static class InvalidSettingValueException extends Exception {
private static final long serialVersionUID = 1L;
+
+ public InvalidSettingValueException() {
+ }
+
+ public InvalidSettingValueException(String message) {
+ super(message);
+ }
}
/**
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java b/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java
index 3d9b26170445dccade571c7ace6325fc2bb93480..f7308902c61e4d0d3cbab7234a2d420aa6ea7221 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java
@@ -253,8 +253,12 @@ public class SettingsImporter {
erroneousAccounts.add(importResult.original);
}
} catch (InvalidSettingValueException e) {
- Timber.e(e, "Encountered invalid setting while importing account \"%s\"",
- account.name);
+ String reason = e.getMessage();
+ if (TextUtils.isEmpty(reason)) {
+ reason = "Unknown";
+ }
+ Timber.e(e, "Encountered invalid setting while importing account \"%s\", reason: \"%s\"",
+ account.name, reason);
erroneousAccounts.add(new AccountDescription(account.name, account.uuid));
} catch (Exception e) {
@@ -363,7 +367,7 @@ public class SettingsImporter {
if (account.incoming == null) {
// We don't import accounts without incoming server settings
- throw new InvalidSettingValueException();
+ throw new InvalidSettingValueException("Missing incoming server settings");
}
// Write incoming server settings
@@ -380,7 +384,7 @@ public class SettingsImporter {
boolean authorizationNeeded = incoming.authenticationType == AuthType.XOAUTH2;
if (account.outgoing == null) {
- throw new InvalidSettingValueException();
+ throw new InvalidSettingValueException("Missing outgoing server settings");
}
String outgoingServerName = null;
@@ -448,7 +452,7 @@ public class SettingsImporter {
importIdentities(editor, contentVersion, uuid, account, overwrite, existingAccount, prefs);
} else if (!mergeImportedAccount) {
// Require accounts to at least have one identity
- throw new InvalidSettingValueException();
+ throw new InvalidSettingValueException("Missing identities, there should be at least one.");
}
// Write folder settings
@@ -541,7 +545,7 @@ public class SettingsImporter {
// Validate email address
if (!IdentitySettingsDescriptions.isEmailAddressValid(identity.email)) {
- throw new InvalidSettingValueException();
+ throw new InvalidSettingValueException("Invalid email address: " + identity.email);
}
// Write email address
diff --git a/app/core/src/test/java/com/fsck/k9/mailstore/MessageStoreManagerTest.kt b/app/core/src/test/java/com/fsck/k9/mailstore/MessageStoreManagerTest.kt
index 98c59cb9a29cfcf1fe6d6b4a042715f7f0bbee5b..89d5e2f626cbd513c441435fe58774d06a6f3e88 100644
--- a/app/core/src/test/java/com/fsck/k9/mailstore/MessageStoreManagerTest.kt
+++ b/app/core/src/test/java/com/fsck/k9/mailstore/MessageStoreManagerTest.kt
@@ -45,7 +45,7 @@ class MessageStoreManagerTest {
assertThat(messageStoreManager.getMessageStore(account)).isSameAs(messageStore2)
}
- private fun ");
- html.append(header);
+ html.append(TextUtils.htmlEncode(header));
html.append(" ");
html.append("");
- html.append(value);
+ html.append(TextUtils.htmlEncode(value));
html.append("
| From: | " + - "from@example.com | " + + "Display name & email address <from@example.com> | " + "
|---|---|---|
| To: | " + - "to@example.com | " + + "Recipient <to@example.com> | " + "
| Sent: | " + "Sat Mar 17 00:00:00 GMT+01:00 2012 | " + "|
| Subject: | " + - "Subject | " + + "Subject with characters that need HTML encoding: "&<> | " + "
" +
diff --git a/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/BodyCleaner.kt b/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/BodyCleaner.kt
index 4fb58c2852248b9e4ba5013d6b3eea02850d3929..9102f3a5ec7c26f89aae641ca9057afd1910cd2b 100644
--- a/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/BodyCleaner.kt
+++ b/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/BodyCleaner.kt
@@ -13,7 +13,7 @@ internal class BodyCleaner {
init {
val allowList = Safelist.relaxed()
- .addTags("font", "hr", "ins", "del", "center", "map", "area", "title", "tt", "kbd", "samp", "var")
+ .addTags("font", "hr", "ins", "del", "center", "map", "area", "title", "tt", "kbd", "samp", "var", "style")
.addAttributes("font", "color", "face", "size")
.addAttributes("a", "name")
.addAttributes("div", "align")
diff --git a/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt b/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt
index 51f9212137470f3c79a66372ddcbde7f33bced6a..bd818122e864ccf75b9d536ce849afd1f8ca2475 100644
--- a/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt
+++ b/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt
@@ -485,6 +485,23 @@ class HtmlSanitizerTest {
assertThat(result.toCompactString()).isEqualTo(html)
}
+ @Test
+ fun `should keep 'style' element in body`() {
+ val html =
+ """
+
+
+
+
+
+
+ """.compactHtml()
+
+ val result = htmlSanitizer.sanitize(html)
+
+ assertThat(result.toCompactString()).isEqualTo(html)
+ }
+
private fun assertTagsNotStripped(element: String) {
val html = """<$element>some text$element>"""
diff --git a/app/k9mail-jmap/src/main/res/values/themes.xml b/app/k9mail-jmap/src/main/res/values/themes.xml
index 04c11fa7b94fe4460e6e08bc7831b27c16df2f6d..e7450e8e1b41a5fc68655957e8cf71a7921c9ea9 100644
--- a/app/k9mail-jmap/src/main/res/values/themes.xml
+++ b/app/k9mail-jmap/src/main/res/values/themes.xml
@@ -130,7 +130,6 @@
- @color/material_purple_500
- #FFC300
- - #ffffffff
- @drawable/ic_person_add
- #ffcccccc
- #e8e8e8
@@ -298,7 +297,6 @@
- @color/material_purple_600
- #FFC300
- - #000000
- @drawable/ic_person_add
- #ff555555
- #313131
diff --git a/app/k9mail/build.gradle.kts b/app/k9mail/build.gradle.kts
index 582a832d53989ec0bbf09ed5f990760c422445f1..194da69d2b2cbd5b66c91c0d0d2ee4674d3f9853 100644
--- a/app/k9mail/build.gradle.kts
+++ b/app/k9mail/build.gradle.kts
@@ -20,8 +20,8 @@ dependencies {
implementation(projects.core.featureflags)
implementation(projects.feature.launcher)
- // TODO remove account setup dependency
implementation(projects.feature.account.setup)
+ implementation(projects.feature.account.edit)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
@@ -52,8 +52,8 @@ android {
applicationId = "foundation.e.mail"
testApplicationId = "foundation.e.mail.tests"
- versionCode = 37010
- versionName = "6.710"
+ versionCode = 37011
+ versionName = "6.711"
// Keep in sync with the resource string array "supported_languages"
resourceConfigurations.addAll(
@@ -86,7 +86,9 @@ android {
buildTypes {
release {
signingConfigs.findByName("release")?.let { releaseSigningConfig ->
- signingConfig = releaseSigningConfig
+ // The comment in the line below is necessary to prevent F-Droid's build tools from breaking our Gradle
+ // config when stripping the signing config.
+ signingConfig = releaseSigningConfig // F-Droid hack
}
isMinifyEnabled = true
@@ -153,7 +155,7 @@ android {
}
}
- packagingOptions {
+ packaging {
jniLibs {
excludes += listOf("kotlin/**")
}
diff --git a/app/k9mail/src/debug/res/values/app_logo_colors.xml b/app/k9mail/src/debug/res/values/app_logo_colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7939c369b4d4880c34421da9ee4f107890d5623f
--- /dev/null
+++ b/app/k9mail/src/debug/res/values/app_logo_colors.xml
@@ -0,0 +1,8 @@
+
+
+ #5917ff
+ #7a45ff
+ #531ad8
+
+ #e3d9ff
+
diff --git a/app/k9mail/src/main/AndroidManifest.xml b/app/k9mail/src/main/AndroidManifest.xml
index 255834b4dc47efb5fb191be6b760e285534e519f..c2e1f5acfdfa28f0f0d2110c72c3ff7d6ca22634 100644
--- a/app/k9mail/src/main/AndroidManifest.xml
+++ b/app/k9mail/src/main/AndroidManifest.xml
@@ -30,8 +30,7 @@
android:allowTaskReparenting="false"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
- android:icon="@drawable/ic_e_launcher"
- android:roundIcon="@mipmap/icon_e"
+ android:icon="@drawable/ic_app_logo"
android:label="@string/app_name"
android:theme="@style/Theme.K9.Startup"
android:resizeableActivity="true"
@@ -61,10 +60,6 @@
-
-
-
-
-
-
{
+ AccountStateLoader(
+ accountManager = get(),
+ )
+ }
+
+ factory {
+ AccountUpdater(
+ preferences = get(),
+ )
+ }
}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/account/AccountStateLoader.kt b/app/k9mail/src/main/java/com/fsck/k9/account/AccountStateLoader.kt
new file mode 100644
index 0000000000000000000000000000000000000000..71b30ec470ee045bcffc0b8c1b7a09148e3b618c
--- /dev/null
+++ b/app/k9mail/src/main/java/com/fsck/k9/account/AccountStateLoader.kt
@@ -0,0 +1,44 @@
+package com.fsck.k9.account
+
+import app.k9mail.feature.account.common.AccountCommonExternalContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import com.fsck.k9.logging.Timber
+import com.fsck.k9.preferences.AccountManager
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import com.fsck.k9.Account as K9Account
+
+class AccountStateLoader(
+ private val accountManager: AccountManager,
+ private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
+) : AccountCommonExternalContract.AccountStateLoader {
+
+ @Suppress("TooGenericExceptionCaught")
+ override suspend fun loadAccountState(accountUuid: String): AccountState? {
+ return try {
+ withContext(coroutineDispatcher) {
+ load(accountUuid)
+ }
+ } catch (e: Exception) {
+ Timber.e(e, "Error while loading account")
+
+ null
+ }
+ }
+
+ private fun load(accountUuid: String): AccountState? {
+ return accountManager.getAccount(accountUuid)?.let { mapToAccountState(it) }
+ }
+
+ private fun mapToAccountState(account: K9Account): AccountState {
+ return AccountState(
+ uuid = account.uuid,
+ emailAddress = account.email,
+ incomingServerSettings = account.incomingServerSettings,
+ outgoingServerSettings = account.outgoingServerSettings,
+ authorizationState = AuthorizationState(account.oAuthState),
+ )
+ }
+}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/account/AccountUpdater.kt b/app/k9mail/src/main/java/com/fsck/k9/account/AccountUpdater.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3a5bd414317b5b42bd9a3fefbaa4b20081439f65
--- /dev/null
+++ b/app/k9mail/src/main/java/com/fsck/k9/account/AccountUpdater.kt
@@ -0,0 +1,44 @@
+package com.fsck.k9.account
+
+import app.k9mail.feature.account.common.domain.entity.Account
+import app.k9mail.feature.account.edit.AccountEditExternalContract
+import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdater.AccountUpdaterResult
+import com.fsck.k9.Preferences
+import com.fsck.k9.logging.Timber
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class AccountUpdater(
+ private val preferences: Preferences,
+ private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
+) : AccountEditExternalContract.AccountUpdater {
+
+ @Suppress("TooGenericExceptionCaught")
+ override suspend fun updateAccount(account: Account): AccountUpdaterResult {
+ return try {
+ withContext(coroutineDispatcher) {
+ AccountUpdaterResult.Success(update(account))
+ }
+ } catch (e: Exception) {
+ Timber.e(e, "Error while updating account")
+
+ AccountUpdaterResult.Error(e.message ?: "Unknown update account error")
+ }
+ }
+
+ private fun update(account: Account): String {
+ val uuid = account.uuid
+ require(uuid.isNotEmpty()) { "Can't update account without uuid" }
+
+ val existingAccount = preferences.getAccount(uuid)
+ require(existingAccount != null) { "Can't update non-existing account" }
+
+ existingAccount.incomingServerSettings = account.incomingServerSettings
+ existingAccount.outgoingServerSettings = account.outgoingServerSettings
+
+ preferences.saveAccount(existingAccount)
+
+ return uuid
+ }
+}
diff --git a/app/k9mail/src/main/java/com/fsck/k9/featureflag/InMemoryFeatureFlagFactory.kt b/app/k9mail/src/main/java/com/fsck/k9/featureflag/InMemoryFeatureFlagFactory.kt
index 2c5eb849490a6a5424119ee5369f39ce72f00eb0..da37d49036a1adedb768c3dc9131f7c60ee1698a 100644
--- a/app/k9mail/src/main/java/com/fsck/k9/featureflag/InMemoryFeatureFlagFactory.kt
+++ b/app/k9mail/src/main/java/com/fsck/k9/featureflag/InMemoryFeatureFlagFactory.kt
@@ -7,7 +7,7 @@ import app.k9mail.core.featureflag.FeatureFlagKey
class InMemoryFeatureFlagFactory : FeatureFlagFactory {
override fun createFeatureCatalog(): List {
return listOf(
- FeatureFlag(FeatureFlagKey("new_onboarding"), false),
+ FeatureFlag(FeatureFlagKey("new_account_edit"), false),
)
}
}
diff --git a/app/k9mail/src/main/res/values/themes.xml b/app/k9mail/src/main/res/values/themes.xml
index 6e393f8cbbf47d50b04485bd4f0f7738df775641..e31b3721876de5b85ee7621efeeab1be641f451b 100644
--- a/app/k9mail/src/main/res/values/themes.xml
+++ b/app/k9mail/src/main/res/values/themes.xml
@@ -119,7 +119,6 @@
- @drawable/ic_messagelist_answered
- @drawable/ic_messagelist_forwarded
- @drawable/ic_messagelist_answered_forwarded
- - @color/color_default_background
- @drawable/ic_person_add
- @color/color_default_foreground
- #ffababab
@@ -267,7 +266,6 @@
- @drawable/ic_messagelist_answered
- @drawable/ic_messagelist_forwarded
- @drawable/ic_messagelist_answered_forwarded
- - @color/color_default_background
- @drawable/ic_person_add
- @color/color_default_foreground
- #ffababab
diff --git a/app/k9mail/src/test/java/com/fsck/k9/DependencyInjectionTest.kt b/app/k9mail/src/test/java/com/fsck/k9/DependencyInjectionTest.kt
index 636d824ab02d66cb7e8e0c93eefcee4cfe8ebd30..005a826beccdc8a97eaad75019837f159389041d 100644
--- a/app/k9mail/src/test/java/com/fsck/k9/DependencyInjectionTest.kt
+++ b/app/k9mail/src/test/java/com/fsck/k9/DependencyInjectionTest.kt
@@ -3,8 +3,8 @@ package com.fsck.k9
import android.view.ContextThemeWrapper
import androidx.lifecycle.LifecycleOwner
import androidx.work.WorkerParameters
-import app.k9mail.feature.account.setup.domain.DomainContract.UseCase.ValidateServerSettings
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
import com.fsck.k9.account.AccountRemoverWorker
import com.fsck.k9.job.MailSyncWorker
import com.fsck.k9.mail.oauth.AuthStateStorage
@@ -21,7 +21,6 @@ import org.junit.runner.RunWith
import org.koin.core.annotation.KoinInternalApi
import org.koin.core.logger.PrintLogger
import org.koin.core.parameter.parametersOf
-import org.koin.core.qualifier.named
import org.koin.java.KoinJavaComponent
import org.koin.test.AutoCloseKoinTest
import org.koin.test.check.checkModules
@@ -59,9 +58,9 @@ class DependencyInjectionTest : AutoCloseKoinTest() {
withParameters(clazz = Class.forName("com.fsck.k9.view.K9WebViewClient").kotlin) {
parametersOf(null, null)
}
- withParameter(named("incoming_validation")) { authStateStorage }
- withParameter(named("outgoing_validation")) { authStateStorage }
- withParameter { authStateStorage }
+ withParameter { authStateStorage }
+ withParameter { authStateStorage }
+ withParameter { authStateStorage }
withParameter { mock() }
}
}
diff --git a/app/k9mail/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt b/app/k9mail/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..be7b5f940271c39f57779bc1c8e5c9cb6fad3fe6
--- /dev/null
+++ b/app/k9mail/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt
@@ -0,0 +1,80 @@
+package com.fsck.k9.account
+
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import assertk.assertions.isNull
+import com.fsck.k9.Account
+import com.fsck.k9.Identity
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.ConnectionSecurity
+import com.fsck.k9.mail.ServerSettings
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class AccountStateLoaderTest {
+
+ @Test
+ fun `loadAccountState() should return null when accountManager returns null`() = runTest {
+ val accountManager = FakeAccountManager()
+ val accountLoader = AccountStateLoader(accountManager)
+
+ val result = accountLoader.loadAccountState("accountUuid")
+
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `loadAccountState() should return account when present in accountManager`() = runTest {
+ val accounts = mapOf(
+ "accountUuid" to Account(uuid = "accountUuid").apply {
+ identities = mutableListOf(Identity())
+ email = "emailAddress"
+ incomingServerSettings = INCOMING_SERVER_SETTINGS
+ outgoingServerSettings = OUTGOING_SERVER_SETTINGS
+ oAuthState = "oAuthState"
+ },
+ )
+ val accountManager = FakeAccountManager(accounts = accounts)
+ val accountLoader = AccountStateLoader(accountManager)
+
+ val result = accountLoader.loadAccountState("accountUuid")
+
+ assertThat(result).isEqualTo(
+ AccountState(
+ uuid = "accountUuid",
+ emailAddress = "emailAddress",
+ incomingServerSettings = INCOMING_SERVER_SETTINGS,
+ outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
+ authorizationState = AuthorizationState("oAuthState"),
+ ),
+ )
+ }
+
+ private companion object {
+ val INCOMING_SERVER_SETTINGS = ServerSettings(
+ type = "imap",
+ host = "imap.example.org",
+ port = 143,
+ connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
+ authenticationType = AuthType.PLAIN,
+ username = "username",
+ password = "password",
+ clientCertificateAlias = null,
+ extra = emptyMap(),
+ )
+
+ val OUTGOING_SERVER_SETTINGS = ServerSettings(
+ type = "smtp",
+ host = "smtp.example.org",
+ port = 587,
+ connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
+ authenticationType = AuthType.PLAIN,
+ username = "username",
+ password = "password",
+ clientCertificateAlias = null,
+ extra = emptyMap(),
+ )
+ }
+}
diff --git a/app/k9mail/src/test/java/com/fsck/k9/account/FakeAccountManager.kt b/app/k9mail/src/test/java/com/fsck/k9/account/FakeAccountManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d22868e2f0b2685560efd7d3f07dd7184f98a9d5
--- /dev/null
+++ b/app/k9mail/src/test/java/com/fsck/k9/account/FakeAccountManager.kt
@@ -0,0 +1,42 @@
+package com.fsck.k9.account
+
+import com.fsck.k9.Account
+import com.fsck.k9.AccountRemovedListener
+import com.fsck.k9.AccountsChangeListener
+import com.fsck.k9.preferences.AccountManager
+import kotlinx.coroutines.flow.Flow
+
+class FakeAccountManager(
+ private val accounts: Map = emptyMap(),
+) : AccountManager {
+
+ override fun getAccountsFlow(): Flow> {
+ TODO("Not yet implemented")
+ }
+
+ override fun getAccount(accountUuid: String): Account? = accounts[accountUuid]
+
+ override fun getAccountFlow(accountUuid: String): Flow {
+ TODO("Not yet implemented")
+ }
+
+ override fun addAccountRemovedListener(listener: AccountRemovedListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun moveAccount(account: Account, newPosition: Int) {
+ TODO("Not yet implemented")
+ }
+
+ override fun addOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun removeOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun saveAccount(account: Account) {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/app/testing/src/main/java/com/fsck/k9/testing/MockHelper.kt b/app/testing/src/main/java/com/fsck/k9/testing/MockHelper.kt
index 17e9e6986e5e593d195b2ed95a3aa6377e9b1e8b..32bca4f250bbc831186535927bc5453fdde9940e 100644
--- a/app/testing/src/main/java/com/fsck/k9/testing/MockHelper.kt
+++ b/app/testing/src/main/java/com/fsck/k9/testing/MockHelper.kt
@@ -17,7 +17,7 @@ object MockHelper {
}
}
- inline fun mockBuilder(stubbing: KStubbing.(T) -> Unit = {}): T {
+ inline fun mockBuilder(stubbing: KStubbing.(T) -> Unit = {}): T {
return mockBuilder(T::class.java).apply { KStubbing(this).stubbing(this) }
}
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/LauncherShortcuts.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/LauncherShortcuts.java
index 78a7ab1ff6a4a6d2ffc85a86a63d8136369c746e..8d8d4a174e85688f2947580a715db86bc15e1635 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/LauncherShortcuts.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/LauncherShortcuts.java
@@ -41,7 +41,7 @@ public class LauncherShortcuts extends AccountList {
displayName = account.getEmail();
}
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
- Parcelable iconResource = Intent.ShortcutIconResource.fromContext(this, R.mipmap.icon_e);
+ Parcelable iconResource = Intent.ShortcutIconResource.fromContext(this, R.drawable.ic_launcher);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
setResult(RESULT_OK, intent);
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java
index c317a558ff583c5a3f39683c2c6d781f77fa0ed2..03ce3181649601276e097ceef501e212fb2c7baa 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java
@@ -268,15 +268,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
LayoutInflater themedLayoutInflater = LayoutInflater.from(themeContext);
contentContainer.setLayoutInflater(themedLayoutInflater);
-
- View contentView = contentContainer.inflate();
-
- // background color needs to be forced
- //TODO: Change themes to use appropriate background colors that don't need overriding.
- TypedValue outValue = new TypedValue();
- themeContext.getTheme().resolveAttribute(R.attr.messageViewBackgroundColor, outValue, true);
-
- contentView.setBackgroundColor(outValue.data);
+ contentContainer.inflate();
initializeActionBar();
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
index 4452e51fa6a7fca7c0096df0b770ea2865366b55..88b85115e075353685d39f52ad015b18c8cfaedd 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
@@ -45,12 +45,9 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import androidx.fragment.app.commitNow
-import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import app.k9mail.core.android.common.contact.CachingRepository
import app.k9mail.core.android.common.contact.ContactRepository
-import app.k9mail.core.featureflag.FeatureFlagKey
-import app.k9mail.core.featureflag.FeatureFlagProvider
import app.k9mail.feature.launcher.FeatureLauncherActivity
import com.fsck.k9.Account
import com.fsck.k9.K9
@@ -86,14 +83,12 @@ import com.fsck.k9.ui.messageview.MessageViewContainerFragment
import com.fsck.k9.ui.messageview.MessageViewContainerFragment.MessageViewContainerListener
import com.fsck.k9.ui.messageview.MessageViewFragment.MessageViewFragmentListener
import com.fsck.k9.ui.messageview.PlaceholderFragment
-import com.fsck.k9.ui.onboarding.OnboardingActivity
import com.fsck.k9.ui.permissions.K9PermissionUiHelper
import com.fsck.k9.ui.permissions.Permission
import com.fsck.k9.ui.permissions.PermissionUiHelper
import com.fsck.k9.view.ViewSwitcher
import com.fsck.k9.view.ViewSwitcher.OnSwitchCompleteListener
import com.mikepenz.materialdrawer.util.getOptimalDrawerWidth
-import java.util.function.Consumer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
@@ -124,7 +119,6 @@ open class MessageList :
private val jobManager: K9JobManager by inject()
private val accountCreatorHelper: AccountCreatorHelper by inject()
private val contactRepository: ContactRepository by inject()
- private val featureFlagProvider: FeatureFlagProvider by inject()
private val permissionUiHelper: PermissionUiHelper = K9PermissionUiHelper(this)
@@ -187,16 +181,7 @@ open class MessageList :
val accounts = preferences.accounts
val hasAccountSetup = accounts.any { it.isFinishedSetup }
if (!hasAccountSetup) {
- generalSettingsManager.setReloadAccountChipsColors(false)
-
- featureFlagProvider.provide(FeatureFlagKey("new_onboarding")).onEnabled {
- FeatureLauncherActivity.launchOnboarding(this)
- }.onDisabled {
- OnboardingActivity.launch(this)
- }.onUnavailable {
- Timber.d("Feature flag 'new_onboarding' is unavailable, falling back to old onboarding")
- OnboardingActivity.launch(this)
- }
+ FeatureLauncherActivity.launchOnboarding(this)
finish()
return
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupAccountType.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupAccountType.kt
index d864fc6dda14e3f79eaf001b7dfcbfebed0f8f4d..bd102cfff50c0e79f2d7cfab8163ec11d69db602 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupAccountType.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupAccountType.kt
@@ -5,7 +5,7 @@ import android.content.Intent
import android.os.Bundle
import android.view.View
import app.k9mail.core.common.mail.Protocols
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase.SuggestServerName
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase.SuggestServerName
import com.fsck.k9.Account
import com.fsck.k9.Preferences
import com.fsck.k9.helper.EmailHelper.getDomainFromEmailAddress
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt
index 68d40beee6c689f102bfb464bd15fe4bdd287fc6..b56f3c3a2dd4a62c3851a871fd56d6e643c7755f 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt
@@ -395,7 +395,7 @@ class AccountSetupBasics : K9Activity() {
preferences.saveAccount(account)
Core.setServicesEnabled(applicationContext)
- AccountSetupNames.actionSetNames(this, account)
+ // AccountSetupNames.actionSetNames(this, account)
}
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java
index 3f789ea71f22a461e5a2a690b0ac2b333723f53e..b95587afc7cfa0d3b3dfeed5a4cd906fabb00765 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupIncoming.java
@@ -26,7 +26,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import app.k9mail.core.common.mail.Protocols;
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase.SuggestServerName;
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase.SuggestServerName;
import com.fsck.k9.Account;
import com.fsck.k9.DI;
import com.fsck.k9.LocalKeyStoreManager;
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupNames.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupNames.java
deleted file mode 100644
index 3c0d672790c038713e7769849da9f7da01762415..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupNames.java
+++ /dev/null
@@ -1,102 +0,0 @@
-
-package com.fsck.k9.activity.setup;
-
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.text.method.TextKeyListener;
-import android.text.method.TextKeyListener.Capitalize;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-
-import com.fsck.k9.Account;
-import com.fsck.k9.Preferences;
-import com.fsck.k9.ui.base.K9Activity;
-import com.fsck.k9.activity.MessageList;
-import com.fsck.k9.ui.R;
-import com.fsck.k9.helper.Utility;
-
-@Deprecated(since = "New account setup flow")
-public class AccountSetupNames extends K9Activity implements OnClickListener {
- private static final String EXTRA_ACCOUNT = "account";
-
- private EditText mDescription;
-
- private EditText mName;
-
- private Account mAccount;
-
- private Button mDoneButton;
-
- public static void actionSetNames(Context context, Account account) {
- Intent i = new Intent(context, AccountSetupNames.class);
- i.putExtra(EXTRA_ACCOUNT, account.getUuid());
- context.startActivity(i);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setLayout(R.layout.account_setup_names);
- setTitle(R.string.account_setup_names_title);
-
- mDescription = findViewById(R.id.account_description);
- mName = findViewById(R.id.account_name);
- mDoneButton = findViewById(R.id.done);
- mDoneButton.setOnClickListener(this);
-
- TextWatcher validationTextWatcher = new TextWatcher() {
- public void afterTextChanged(Editable s) {
- validateFields();
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
- };
- mName.addTextChangedListener(validationTextWatcher);
-
- mName.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
-
- String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
- mAccount = Preferences.getPreferences().getAccount(accountUuid);
-
- String senderName = mAccount.getSenderName();
- if (senderName != null) {
- mName.setText(senderName);
- }
-
- if (!Utility.requiredFieldValid(mName)) {
- mDoneButton.setEnabled(false);
- }
- }
-
- private void validateFields() {
- mDoneButton.setEnabled(Utility.requiredFieldValid(mName));
- Utility.setCompoundDrawablesAlpha(mDoneButton, mDoneButton.isEnabled() ? 255 : 128);
- }
-
- protected void onNext() {
- if (Utility.requiredFieldValid(mDescription)) {
- mAccount.setName(mDescription.getText().toString());
- }
- mAccount.setSenderName(mName.getText().toString());
- mAccount.markSetupFinished();
- Preferences.getPreferences().saveAccount(mAccount);
- finishAffinity();
- MessageList.launch(this, mAccount);
- }
-
- public void onClick(View v) {
- if (v.getId() == R.id.done) {
- onNext();
- }
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOptions.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOptions.java
deleted file mode 100644
index 43fd54f798b2c49e76b3535e567c3d97d4b5a6dd..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOptions.java
+++ /dev/null
@@ -1,126 +0,0 @@
-
-package com.fsck.k9.activity.setup;
-
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.Spinner;
-
-import com.fsck.k9.Account;
-import com.fsck.k9.Core;
-import com.fsck.k9.Preferences;
-import com.fsck.k9.ui.R;
-import com.fsck.k9.ui.base.K9Activity;
-
-
-@Deprecated(since = "New account setup flow")
-public class AccountSetupOptions extends K9Activity implements OnClickListener {
- private static final String EXTRA_ACCOUNT = "account";
-
- private Spinner mCheckFrequencyView;
-
- private Spinner mDisplayCountView;
-
-
- private CheckBox mNotifyView;
-
- private Account mAccount;
-
- public static void actionOptions(Context context, Account account) {
- Intent i = new Intent(context, AccountSetupOptions.class);
- i.putExtra(EXTRA_ACCOUNT, account.getUuid());
- context.startActivity(i);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setLayout(R.layout.account_setup_options);
- setTitle(R.string.account_setup_options_title);
-
- mCheckFrequencyView = findViewById(R.id.account_check_frequency);
- mDisplayCountView = findViewById(R.id.account_display_count);
- mNotifyView = findViewById(R.id.account_notify);
-
- findViewById(R.id.next).setOnClickListener(this);
-
- SpinnerOption checkFrequencies[] = {
- new SpinnerOption(-1,
- getString(R.string.account_setup_options_mail_check_frequency_never)),
- new SpinnerOption(5,
- getString(R.string.account_setup_options_mail_check_frequency_5min)),
- new SpinnerOption(15,
- getString(R.string.account_setup_options_mail_check_frequency_15min)),
- new SpinnerOption(30,
- getString(R.string.account_setup_options_mail_check_frequency_30min)),
- new SpinnerOption(60,
- getString(R.string.account_setup_options_mail_check_frequency_1hour)),
- new SpinnerOption(120,
- getString(R.string.account_setup_options_mail_check_frequency_2hour)),
- new SpinnerOption(180,
- getString(R.string.account_setup_options_mail_check_frequency_3hour)),
- new SpinnerOption(360,
- getString(R.string.account_setup_options_mail_check_frequency_6hour)),
- new SpinnerOption(720,
- getString(R.string.account_setup_options_mail_check_frequency_12hour)),
- new SpinnerOption(1440,
- getString(R.string.account_setup_options_mail_check_frequency_24hour)),
-
- };
-
- ArrayAdapter checkFrequenciesAdapter = new ArrayAdapter<>(this,
- android.R.layout.simple_spinner_item, checkFrequencies);
- checkFrequenciesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mCheckFrequencyView.setAdapter(checkFrequenciesAdapter);
-
- SpinnerOption displayCounts[] = {
- new SpinnerOption(10, getString(R.string.account_setup_options_mail_display_count_10)),
- new SpinnerOption(25, getString(R.string.account_setup_options_mail_display_count_25)),
- new SpinnerOption(50, getString(R.string.account_setup_options_mail_display_count_50)),
- new SpinnerOption(100, getString(R.string.account_setup_options_mail_display_count_100)),
- new SpinnerOption(250, getString(R.string.account_setup_options_mail_display_count_250)),
- new SpinnerOption(500, getString(R.string.account_setup_options_mail_display_count_500)),
- new SpinnerOption(1000, getString(R.string.account_setup_options_mail_display_count_1000)),
- };
-
- ArrayAdapter displayCountsAdapter = new ArrayAdapter<>(this,
- android.R.layout.simple_spinner_item, displayCounts);
- displayCountsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mDisplayCountView.setAdapter(displayCountsAdapter);
-
- String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
- mAccount = Preferences.getPreferences().getAccount(accountUuid);
-
- mNotifyView.setChecked(mAccount.isNotifyNewMail());
- SpinnerOption.setSpinnerOptionValue(mCheckFrequencyView, mAccount
- .getAutomaticCheckIntervalMinutes());
- SpinnerOption.setSpinnerOptionValue(mDisplayCountView, mAccount
- .getDisplayCount());
- }
-
- private void onDone() {
- mAccount.setName(mAccount.getEmail());
- mAccount.setNotifyNewMail(mNotifyView.isChecked());
- mAccount.setAutomaticCheckIntervalMinutes((Integer)((SpinnerOption)mCheckFrequencyView
- .getSelectedItem()).value);
- mAccount.setDisplayCount((Integer)((SpinnerOption)mDisplayCountView
- .getSelectedItem()).value);
-
- mAccount.setFolderPushMode(Account.FolderMode.NONE);
-
- Preferences.getPreferences().saveAccount(mAccount);
- Core.setServicesEnabled(this);
- AccountSetupNames.actionSetNames(this, mAccount);
- }
-
- public void onClick(View v) {
- if (v.getId() == R.id.next) {
- onDone();
- }
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
index d2b6d3a354921de779fd36e959122e117f81e823..86bfaf37a2dbe8d2d4e1715ac4bfc8becef43454 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupOutgoing.java
@@ -495,7 +495,7 @@ public class AccountSetupOutgoing extends K9Activity implements OnClickListener,
Preferences.getPreferences().saveAccount(mAccount);
finish();
} else {
- AccountSetupOptions.actionOptions(this, mAccount);
+// AccountSetupOptions.actionOptions(this, mAccount);
}
}
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt
index d4278c660b32daa553ebb7d638b354239e897d44..8c1385742878cdeca59c44d80bcf312c3677df78 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AuthViewModel.kt
@@ -13,7 +13,7 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase.GetOAuthRequestIntent
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase.GetOAuthRequestIntent
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationIntentResult
import com.fsck.k9.Account
import com.fsck.k9.preferences.AccountManager
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/SpinnerOption.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/SpinnerOption.java
deleted file mode 100644
index 89275b97424b8b0e8bd3343e45df1495c170155d..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/SpinnerOption.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- *
- */
-
-package com.fsck.k9.activity.setup;
-
-import android.widget.Spinner;
-
-public class SpinnerOption {
- public Object value;
-
- public String label;
-
- public static void setSpinnerOptionValue(Spinner spinner, Object value) {
- for (int i = 0, count = spinner.getCount(); i < count; i++) {
- SpinnerOption so = (SpinnerOption)spinner.getItemAtPosition(i);
- if (so.value.equals(value)) {
- spinner.setSelection(i, true);
- return;
- }
- }
- }
-
- public SpinnerOption(Object value, String label) {
- this.value = value;
- this.label = label;
- }
-
- @Override
- public String toString() {
- return label;
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt
index 9a9f5dbf993f0cca69abb52798d3cf84bc0e8140..a15e49f52f219babf2b1844f73ef4adf6b58e2e4 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt
@@ -13,8 +13,8 @@ import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.OnClickListener
-import android.view.View.VISIBLE
import android.view.View.OnLongClickListener
+import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
@@ -50,16 +50,19 @@ class MessageListAdapter internal constructor(
private val contactsPictureLoader: ContactPictureLoader,
private val listItemListener: MessageListItemActionListener,
private val appearance: MessageListAppearance,
- private val relativeDateTimeFormatter: RelativeDateTimeFormatter
+ private val relativeDateTimeFormatter: RelativeDateTimeFormatter,
) : RecyclerView.Adapter() {
private val forwardedIcon: Drawable = theme.resolveDrawableAttribute(R.attr.messageListForwarded)
private val answeredIcon: Drawable = theme.resolveDrawableAttribute(R.attr.messageListAnswered)
private val forwardedAnsweredIcon: Drawable = theme.resolveDrawableAttribute(R.attr.messageListAnsweredForwarded)
- private val activeItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListActiveItemBackgroundColor)
- private val selectedItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListSelectedBackgroundColor)
+ private val activeItemBackgroundColor: Int =
+ theme.resolveColorAttribute(R.attr.messageListActiveItemBackgroundColor)
+ private val selectedItemBackgroundColor: Int =
+ theme.resolveColorAttribute(R.attr.messageListSelectedBackgroundColor)
private val previewTextColor: Int = theme.resolveColorAttribute(R.attr.messageListPreviewTextColor)
- private val regularItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListRegularItemBackgroundColor)
+ private val regularItemBackgroundColor: Int =
+ theme.resolveColorAttribute(R.attr.messageListRegularItemBackgroundColor)
private val readItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListReadItemBackgroundColor)
private val unreadItemBackgroundColor: Int =
theme.resolveColorAttribute(R.attr.messageListUnreadItemBackgroundColor)
@@ -74,7 +77,6 @@ class MessageListAdapter internal constructor(
private val relaxedTextViewMarginTop = res.getDimensionPixelSize(R.dimen.messageListRelaxedTextViewMargin)
private val relaxedLineSpacingMultiplier = res.getFloatCompat(R.dimen.messageListRelaxedLineSpacingMultiplier)
-
var messages: List = emptyList()
@SuppressLint("NotifyDataSetChanged")
set(value) {
@@ -89,7 +91,7 @@ class MessageListAdapter internal constructor(
}
val diffResult = DiffUtil.calculateDiff(
- MessageListDiffCallback(oldMessageList = oldMessageList, newMessageList = value)
+ MessageListDiffCallback(oldMessageList = oldMessageList, newMessageList = value),
)
diffResult.dispatchUpdatesTo(this)
}
@@ -284,7 +286,7 @@ class MessageListAdapter internal constructor(
appearance.fontSizes.setViewTextSize(holder.preview, appearance.fontSizes.messageListPreview)
appearance.fontSizes.setViewTextSize(
holder.threadCount,
- appearance.fontSizes.messageListSubject
+ appearance.fontSizes.messageListSubject,
) // thread count is next to subject
holder.flagged.isVisible = false
@@ -306,11 +308,13 @@ class MessageListAdapter internal constructor(
textViewMarginTop = compactTextViewMarginTop
lineSpacingMultiplier = compactLineSpacingMultiplier
}
+
UiDensity.Default -> {
verticalPadding = defaultVerticalPadding
textViewMarginTop = defaultTextViewMarginTop
lineSpacingMultiplier = defaultLineSpacingMultiplier
}
+
UiDensity.Relaxed -> {
verticalPadding = relaxedVerticalPadding
textViewMarginTop = relaxedTextViewMarginTop
@@ -338,9 +342,11 @@ class MessageListAdapter internal constructor(
val messageListItem = getItem(position)
bindMessageViewHolder(holder as MessageViewHolder, messageListItem)
}
+
TYPE_FOOTER -> {
bindFooterViewHolder(holder as FooterViewHolder)
}
+
else -> {
error("Unsupported type: $viewType")
}
@@ -443,7 +449,7 @@ class MessageListAdapter internal constructor(
preview: TextView,
beforePreviewText: CharSequence,
sigil: String,
- messageRead: Boolean
+ messageRead: Boolean,
) {
val displayText = preview.text as Spannable
addBeforePreviewSpan(displayText, displayText.length, messageRead)
@@ -598,7 +604,7 @@ private fun View.setMarginTop(margin: Int) {
private class MessageListDiffCallback(
private val oldMessageList: List,
- private val newMessageList: List
+ private val newMessageList: List,
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldMessageList.size
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt
index 848d8089fca403d7e6b00165693166c2ecc3e4d6..84de6c46dce25812f1a862565022bc4031dc9d99 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt
@@ -184,7 +184,7 @@ class MessageViewContainerFragment : Fragment() {
val newPosition = viewPager.currentItem - 1
return if (newPosition >= 0) {
setActiveMessage(newPosition)
- viewPager.setCurrentItem(newPosition, /* smoothScroll = */ false)
+ viewPager.setCurrentItem(newPosition, false)
true
} else {
false
@@ -195,7 +195,7 @@ class MessageViewContainerFragment : Fragment() {
val newPosition = viewPager.currentItem + 1
return if (newPosition < adapter.itemCount) {
setActiveMessage(newPosition)
- viewPager.setCurrentItem(newPosition, /* smoothScroll = */ false)
+ viewPager.setCurrentItem(newPosition, false)
true
} else {
false
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt
index f8f2c4ddf56b091912cc04081a107ef93beda022..bd500573e594b383579da406cd8f2fa041796944 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt
@@ -1,7 +1,5 @@
package com.fsck.k9.ui.messageview
-import android.text.SpannableStringBuilder
-
private const val LIST_SEPARATOR = ", "
/**
@@ -19,7 +17,7 @@ private const val LIST_SEPARATOR = ", "
internal class RecipientLayoutCreator(
private val textMeasure: TextMeasure,
private val maxNumberOfRecipientNames: Int,
- private val recipientsPrefix: String,
+ private val recipientsFormat: String,
private val additionalRecipientSpacing: Int,
private val additionalRecipientsPrefix: String,
) {
@@ -30,14 +28,9 @@ internal class RecipientLayoutCreator(
): RecipientLayoutData {
require(recipientNames.isNotEmpty())
- val displayRecipientsBuilder = SpannableStringBuilder()
-
if (recipientNames.size == 1) {
- displayRecipientsBuilder.append(recipientsPrefix)
- displayRecipientsBuilder.append(recipientNames.first())
-
return RecipientLayoutData(
- recipientNames = displayRecipientsBuilder,
+ recipientNames = recipientsFormat.format(recipientNames.first()),
additionalRecipients = null,
)
}
@@ -46,12 +39,11 @@ internal class RecipientLayoutCreator(
val maxRecipientNames = recipientNames.size.coerceAtMost(maxNumberOfRecipientNames)
for (numberOfDisplayRecipients in maxRecipientNames downTo 2) {
- displayRecipientsBuilder.clear()
- displayRecipientsBuilder.append(recipientsPrefix)
-
- recipientNames.asSequence()
+ val recipientNamesString = recipientNames.asSequence()
.take(numberOfDisplayRecipients)
- .joinTo(displayRecipientsBuilder, separator = LIST_SEPARATOR)
+ .joinToString(separator = LIST_SEPARATOR)
+
+ val displayRecipients = recipientsFormat.format(recipientNamesString)
additionalRecipientsBuilder.setLength(0)
val numberOfAdditionalRecipients = totalNumberOfRecipients - numberOfDisplayRecipients
@@ -60,20 +52,16 @@ internal class RecipientLayoutCreator(
additionalRecipientsBuilder.append(numberOfAdditionalRecipients)
}
- if (doesTextFitAvailableWidth(displayRecipientsBuilder, additionalRecipientsBuilder, availableWidth)) {
+ if (doesTextFitAvailableWidth(displayRecipients, additionalRecipientsBuilder, availableWidth)) {
return RecipientLayoutData(
- recipientNames = displayRecipientsBuilder,
+ recipientNames = displayRecipients,
additionalRecipients = additionalRecipientsBuilder.toStringOrNull(),
)
}
}
- displayRecipientsBuilder.clear()
- displayRecipientsBuilder.append(recipientsPrefix)
- displayRecipientsBuilder.append(recipientNames.first())
-
return RecipientLayoutData(
- recipientNames = displayRecipientsBuilder,
+ recipientNames = recipientsFormat.format(recipientNames.first()),
additionalRecipients = "$additionalRecipientsPrefix${totalNumberOfRecipients - 1}",
)
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt
index 5c82354de2b189c3bacd70b277cc868ca2c9c4ec..3753b0322122261d4de716b982abf27fa5345052 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt
@@ -68,7 +68,7 @@ class RecipientNamesView(context: Context, attrs: AttributeSet?) : ViewGroup(con
recipientLayoutCreator = RecipientLayoutCreator(
textMeasure = textMeasure,
maxNumberOfRecipientNames = MAX_NUMBER_OF_RECIPIENT_NAMES,
- recipientsPrefix = context.getString(R.string.message_view_recipient_prefix),
+ recipientsFormat = context.getString(R.string.message_view_recipients_format),
additionalRecipientSpacing = additionRecipientSpacing,
additionalRecipientsPrefix = context.getString(R.string.message_view_additional_recipient_prefix),
)
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/OnboardingActivity.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/OnboardingActivity.kt
deleted file mode 100644
index 401e022a0e0dc5edc21de0af9c8b1bfd4a3a8b67..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/OnboardingActivity.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.fsck.k9.ui.onboarding
-
-import android.app.Activity
-import android.content.Intent
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
-import android.os.Bundle
-import androidx.appcompat.widget.Toolbar
-import androidx.core.content.ContextCompat
-import androidx.navigation.NavController
-import androidx.navigation.ui.AppBarConfiguration
-import androidx.navigation.ui.setupActionBarWithNavController
-import com.fsck.k9.ui.R
-import com.fsck.k9.ui.base.K9Activity
-import com.fsck.k9.ui.base.extensions.findNavController
-
-class OnboardingActivity : K9Activity() {
- private lateinit var navController: NavController
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setLayout(R.layout.activity_onboarding)
-
- initializeActionBar()
- }
-
- private fun initializeActionBar() {
- val appBarConfiguration = AppBarConfiguration(topLevelDestinationIds = setOf(R.id.welcomeScreen))
-
- navController = findNavController(R.id.nav_host_fragment)
- setupActionBarWithNavController(navController, appBarConfiguration)
- val toolbar = findViewById(R.id.toolbar)
- toolbar.navigationIcon?.mutate()?.colorFilter = PorterDuffColorFilter(ContextCompat.getColor(this, R.color.color_default_primary_text), PorterDuff.Mode.SRC_OVER)
- }
-
- override fun onSupportNavigateUp(): Boolean {
- return navController.navigateUp() || super.onSupportNavigateUp()
- }
-
- companion object {
- @JvmStatic
- fun launch(activity: Activity) {
- val intent = Intent(activity, OnboardingActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- }
- activity.startActivity(intent)
- }
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt
deleted file mode 100644
index 25943401f618b3bbaeccd1812bbfd8e4cbc7aa46..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.fsck.k9.ui.onboarding
-
-import android.os.Bundle
-import android.text.method.LinkMovementMethod
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.appcompat.app.ActionBar
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.navigation.fragment.findNavController
-import com.fsck.k9.ui.R
-import com.fsck.k9.ui.helper.HtmlToSpanned
-import com.fsck.k9.ui.observeNotNull
-import com.fsck.k9.ui.settings.import.SettingsImportResultViewModel
-import com.fsck.k9.ui.settings.import.SettingsImportSuccess
-import org.koin.android.ext.android.inject
-import org.koin.androidx.viewmodel.ext.android.activityViewModel
-
-class WelcomeFragment : Fragment() {
- private val htmlToSpanned: HtmlToSpanned by inject()
- private val importResultViewModel: SettingsImportResultViewModel by activityViewModel()
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.fragment_welcome_message, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val welcome: TextView = view.findViewById(R.id.welcome_message)
- welcome.text = htmlToSpanned.convert(getString(R.string.welcome_message_text))
- welcome.movementMethod = LinkMovementMethod.getInstance()
-
- view.findViewById(R.id.next).setOnClickListener { launchAccountSetup() }
- view.findViewById(R.id.import_settings).setOnClickListener { launchImportSettings() }
-
- importResultViewModel.settingsImportResult.observeNotNull(this) {
- if (it == SettingsImportSuccess) {
- launchMessageList()
- }
- }
- }
-
- override fun onResume() {
- super.onResume()
- getActionBar()?.hide()
- }
-
- override fun onStop() {
- getActionBar()?.show()
- super.onStop()
- }
-
- private fun getActionBar(): ActionBar? {
- if (activity is AppCompatActivity) {
- return (activity as AppCompatActivity).supportActionBar
- }
-
- return null
- }
-
- private fun launchAccountSetup() {
- findNavController().navigate(R.id.action_welcomeScreen_to_addAccountScreen)
- requireActivity().finish()
- }
-
- private fun launchImportSettings() {
- findNavController().navigate(R.id.action_welcomeScreen_to_settingsImportScreen)
- }
-
- private fun launchMessageList() {
- findNavController().navigate(R.id.action_welcomeScreen_to_messageListScreen)
- requireActivity().finish()
- }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsListFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsListFragment.kt
index 1f56f7e75767e03e02e296566845a2cba9dbc67f..1c87c21ff472ff9a37bac4faebf882821b2449ad 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsListFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsListFragment.kt
@@ -15,9 +15,6 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import app.k9mail.core.featureflag.FeatureFlagKey
-import app.k9mail.core.featureflag.FeatureFlagProvider
-import app.k9mail.core.featureflag.FeatureFlagResult
import app.k9mail.feature.launcher.FeatureLauncherActivity
import com.fsck.k9.Account
import com.fsck.k9.ui.R
@@ -32,13 +29,10 @@ import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.drag.ItemTouchCallback
import com.mikepenz.fastadapter.drag.SimpleDragCallback
import com.mikepenz.fastadapter.utils.DragDropUtil
-import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
-import timber.log.Timber
class SettingsListFragment : Fragment(), ItemTouchCallback {
private val viewModel: SettingsViewModel by viewModel()
- private val featureFlagProvider: FeatureFlagProvider by inject()
private lateinit var itemAdapter: ItemAdapter
@@ -144,10 +138,7 @@ class SettingsListFragment : Fragment(), ItemTouchCallback {
is AccountItem -> launchAccountSettings(item.account)
is UrlActionItem -> openUrl(item.url)
is SettingsActionItem -> {
- if (
- item.navigationAction == R.id.action_settingsListScreen_to_addAccountScreen &&
- featureFlagProvider.provide(FeatureFlagKey("new_onboarding")) is FeatureFlagResult.Enabled
- ) {
+ if (item.navigationAction == R.id.action_settingsListScreen_to_addAccountScreen) {
FeatureLauncherActivity.launchSetupAccount(requireActivity())
} else {
findNavController().navigate(item.navigationAction)
@@ -170,19 +161,9 @@ class SettingsListFragment : Fragment(), ItemTouchCallback {
}
private fun launchOnboarding() {
- featureFlagProvider.provide(FeatureFlagKey("new_onboarding")).onEnabled {
- FeatureLauncherActivity.launchOnboarding(requireActivity())
- }.onDisabled {
- launchOldOnboarding()
- }.onUnavailable {
- Timber.d("Feature flag 'new_onboarding' is unavailable, falling back to old onboarding")
- launchOldOnboarding()
- }
- requireActivity().finishAffinity()
- }
+ FeatureLauncherActivity.launchOnboarding(requireActivity())
- private fun launchOldOnboarding() {
- findNavController().navigate(R.id.action_settingsListScreen_to_addAccountScreen)
+ requireActivity().finishAffinity()
}
private fun buildSettingsList(block: SettingsListBuilder.() -> Unit): List {
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt
index fdc4d1ee52cafbb7aefb912ff88a8f9d2d43da51..dff2db9dcf0a07ff8f6e98508f1747a33a812fc2 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt
@@ -13,6 +13,9 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.SwitchPreference
+import app.k9mail.core.featureflag.FeatureFlagProvider
+import app.k9mail.core.featureflag.toFeatureFlagKey
+import app.k9mail.feature.launcher.FeatureLauncherActivity
import com.fsck.k9.Account
import com.fsck.k9.account.BackgroundAccountRemover
import com.fsck.k9.activity.ManageIdentities
@@ -52,6 +55,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
private val notificationChannelManager: NotificationChannelManager by inject()
private val notificationSettingsUpdater: NotificationSettingsUpdater by inject()
private val vibrator: Vibrator by inject()
+ private val featureFlagProvider: FeatureFlagProvider by inject()
private lateinit var dataStore: AccountSettingsDataStore
@@ -123,13 +127,16 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
onDeleteAccount()
true
}
+
else -> super.onOptionsItemSelected(item)
}
}
private fun initializeIncomingServer() {
findPreference(PREFERENCE_INCOMING_SERVER)?.onClick {
- AccountSetupIncoming.actionEditIncomingSettings(requireActivity(), accountUuid)
+ featureFlagProvider.provide("new_account_edit".toFeatureFlagKey())
+ .onEnabled { FeatureLauncherActivity.launchEditIncomingSettings(requireActivity(), accountUuid) }
+ .onDisabled { AccountSetupIncoming.actionEditIncomingSettings(requireActivity(), accountUuid) }
}
}
@@ -155,7 +162,9 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
private fun initializeOutgoingServer() {
findPreference(PREFERENCE_OUTGOING_SERVER)?.onClick {
- AccountSetupOutgoing.actionEditOutgoingSettings(requireActivity(), accountUuid)
+ featureFlagProvider.provide("new_account_edit".toFeatureFlagKey())
+ .onEnabled { FeatureLauncherActivity.launchEditOutgoingSettings(requireActivity(), accountUuid) }
+ .onDisabled { AccountSetupOutgoing.actionEditOutgoingSettings(requireActivity(), accountUuid) }
}
}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageWebView.kt b/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageWebView.kt
index 1fff558cae8d30241e5957e2f9c5b136950021f4..603392d85bd153359a077b172757cb221fb78127 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageWebView.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageWebView.kt
@@ -3,6 +3,7 @@ package com.fsck.k9.view
import android.content.Context
import android.content.pm.PackageManager
import android.util.AttributeSet
+import android.util.TypedValue
import android.webkit.WebSettings.LayoutAlgorithm
import android.webkit.WebSettings.RenderPriority
import android.webkit.WebView
@@ -36,7 +37,10 @@ class MessageWebView : WebView, KoinComponent {
isLongClickable = true
if (config.useDarkMode) {
- setBackgroundColor(0xff000000L.toInt())
+ val typedValue = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.windowBackground, typedValue, true)
+ val backgroundColor = typedValue.data
+ setBackgroundColor(backgroundColor)
}
with(settings) {
diff --git a/app/ui/legacy/src/main/res/mipmap-anydpi-v26/ic_e_launcher.xml b/app/ui/legacy/src/main/res/drawable/ic_app_logo.xml
similarity index 98%
rename from app/ui/legacy/src/main/res/mipmap-anydpi-v26/ic_e_launcher.xml
rename to app/ui/legacy/src/main/res/drawable/ic_app_logo.xml
index 1816bb23a6ca367653e3682b6e73384c15dc94d6..bc056f2e186d2884e29dc5aab0cfecdb047c6450 100644
--- a/app/ui/legacy/src/main/res/mipmap-anydpi-v26/ic_e_launcher.xml
+++ b/app/ui/legacy/src/main/res/drawable/ic_app_logo.xml
@@ -19,4 +19,4 @@
-
\ No newline at end of file
+
diff --git a/app/ui/legacy/src/main/res/drawable/ic_help.xml b/app/ui/legacy/src/main/res/drawable/ic_help.xml
index 95af104a2335b792a969a4c3d05067255e5bd45b..c8e72a555e64beff28221df5cc027949f5d9fe89 100644
--- a/app/ui/legacy/src/main/res/drawable/ic_help.xml
+++ b/app/ui/legacy/src/main/res/drawable/ic_help.xml
@@ -1,10 +1,9 @@
diff --git a/app/ui/legacy/src/main/res/drawable/ic_open_book.xml b/app/ui/legacy/src/main/res/drawable/ic_open_book.xml
index 99417448fc537083ff5659959caef34490700417..cd4a6fc2bc7a48a07eee62b4449ece4b1c8fd7ef 100644
--- a/app/ui/legacy/src/main/res/drawable/ic_open_book.xml
+++ b/app/ui/legacy/src/main/res/drawable/ic_open_book.xml
@@ -1,10 +1,9 @@
diff --git a/app/ui/legacy/src/main/res/layout/account_setup_names.xml b/app/ui/legacy/src/main/res/layout/account_setup_names.xml
deleted file mode 100644
index 2a9f68c12b26aa2d9f4e0e853f50aa8d07762966..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/res/layout/account_setup_names.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/ui/legacy/src/main/res/layout/account_setup_options.xml b/app/ui/legacy/src/main/res/layout/account_setup_options.xml
deleted file mode 100644
index d15bc14c906db7d72b20f260535f1a3d2181eb67..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/res/layout/account_setup_options.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/ui/legacy/src/main/res/layout/activity_onboarding.xml b/app/ui/legacy/src/main/res/layout/activity_onboarding.xml
deleted file mode 100644
index fc665332bda0bee122ed67e90ee8dc36fa89276f..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/res/layout/activity_onboarding.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/ui/legacy/src/main/res/layout/fragment_about.xml b/app/ui/legacy/src/main/res/layout/fragment_about.xml
index 5eed2b93887557f864f81c5d3206f2d71db3f2ce..8292c82dd02409cfe22705c7ebf5b60df982386f 100644
--- a/app/ui/legacy/src/main/res/layout/fragment_about.xml
+++ b/app/ui/legacy/src/main/res/layout/fragment_about.xml
@@ -17,7 +17,7 @@
android:layout_gravity="center"
android:layout_margin="16dp"
android:contentDescription="@null"
- app:srcCompat="@mipmap/icon_e" />
+ app:srcCompat="@drawable/ic_app_logo" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/ui/legacy/src/main/res/layout/message.xml b/app/ui/legacy/src/main/res/layout/message.xml
index 83ceb730a8f00fea42f7ea6b9525ca5aa8667913..37f9cec3994279b8a6dd4ca7b33e91819c405c2a 100644
--- a/app/ui/legacy/src/main/res/layout/message.xml
+++ b/app/ui/legacy/src/main/res/layout/message.xml
@@ -1,4 +1,8 @@
+
+
diff --git a/app/ui/legacy/src/main/res/layout/message_view_header.xml b/app/ui/legacy/src/main/res/layout/message_view_header.xml
index 749f3e551726e77325c68ddff8dde42e34c53c1b..410e34fd8f18ea2ab990fd2434fbc96d5a90ceed 100644
--- a/app/ui/legacy/src/main/res/layout/message_view_header.xml
+++ b/app/ui/legacy/src/main/res/layout/message_view_header.xml
@@ -163,6 +163,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@color/color_default_background"
+ android:contentDescription="@string/reply_action"
android:paddingHorizontal="12dp"
app:layout_constraintEnd_toStartOf="@id/menu_overflow"
app:layout_constraintTop_toTopOf="@+id/menu_overflow"
@@ -180,6 +181,7 @@
android:background="@color/color_default_background"
android:clickable="false"
android:focusable="false"
+ android:contentDescription="@string/abc_action_menu_overflow_description"
android:paddingStart="6dp"
android:paddingEnd="10dp"
android:visibility="invisible"
diff --git a/app/ui/legacy/src/main/res/navigation/navigation_onboarding.xml b/app/ui/legacy/src/main/res/navigation/navigation_onboarding.xml
deleted file mode 100644
index dcddab15860df3f194ce5c25b16bdb80d8fc292a..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/res/navigation/navigation_onboarding.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/ui/legacy/src/main/res/raw/changelog_master.xml b/app/ui/legacy/src/main/res/raw/changelog_master.xml
index f204abf9630791dd8f8b5b90d69412970a6c0d51..ad0482c6e0c91f997266d626e3399028bc118490 100644
--- a/app/ui/legacy/src/main/res/raw/changelog_master.xml
+++ b/app/ui/legacy/src/main/res/raw/changelog_master.xml
@@ -5,6 +5,16 @@
Locale-specific versions are kept in res/raw-/changelog.xml.
-->
+
+ Simplified the app icon so it can be a vector drawable
+ Improved screen reader experience in various places
+ Improved logging on failing settings import
+ Improved display of some HTML messages
+ Changed background color in message view and compose screens when using dark theme
+ Fixed OAuth 2.0 with Yahoo and AOL
+ Fixed display issues when rendering a message/rfc822 inline part
+ Updated translations
+
Fixed bug where accounts using OAuth weren't set up properly in K-9 Mail 6.709
Moved "Show only subscribed folders" setting to "Folders" section
diff --git a/app/ui/legacy/src/main/res/values-ar/strings.xml b/app/ui/legacy/src/main/res/values-ar/strings.xml
index 67321d2e5eb9528d923d3d2ca8fa243526f869da..a453b84f001385df0fdc4617b51f2b8d98268c17 100644
--- a/app/ui/legacy/src/main/res/values-ar/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ar/strings.xml
@@ -15,9 +15,6 @@
مالجديد
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -257,9 +254,6 @@
الإعداد يدويا
المصادقة \u2026
جارٍ الإلغاء\u2026
- لقد انتهيت تقريبًا
- اَعطِ هذا الحساب اسمًا (اختياري):
- اكتب اسمك (تظهر في الرسائل الصادرة):
نوع الحساب
ما نوع هذا الحساب؟
POP3
@@ -303,7 +297,6 @@
اسم المُستخدم
كلمة السِّر
المصادقة
- خيارات الحساب
أبدًا
كل 15 دقيقة
كل 30 دقيقة
@@ -321,7 +314,6 @@
كل 36 دقيقة
كل 48 دقيقة
كل 60 دقيقة
- نبهني عند وصول رسالة
عدد الرسائل التي ستظهر
10 رسائل
25 رسالة
@@ -786,337 +778,337 @@
عناوين البريد
- الكود المصدري
- مشروع مفتوح المصدر
- الموقع الإلكتروني
- دليل المستخدم
- الحصول علي مساعدة
- منتدى المستخدم
- Fediverse
- تويتر
- تعذر تحميل سجل التغيير
- الإصدار %s
- إظهار التغييرات الأخيرة عندما تم تحديث التطبيق
-
- اكتشف الجديد في هذا الإصدار
-
- عرض
-
- نقل إلي
- نسخ إلي
- الرسائل الجديدة
- إلغاء الاشتراك
- تم نسخ نص الموضوع إلى الحافظة
- +%1$d المزيد علي %2$s
- خطأ في الشهادة
-
- إشعار خطأ
-
- حدث خطأ أثناء محاولة إنشاء إشعار نظام برسالة جديدة. السبب على الأرجح هو فقدان صوت الإشعار. \ n \ n انقر لفتح إعدادات الإشعارات.
- تزامن (إرسال)
- يتم عرضها أثناء انتظار رسائل جديدة
- سجلات التصدير
- تم التصدير بنجاح. قد تحتوي السجلات على معلومات حساسة. كن حذرًا لمن ترسلهم إليه.
- فشل التصدير.
- يحتوي حقل المستلم على إدخال غير مكتمل!
-
- إلي
-
- +
-
- انا
-
- عرض الصور البعيدة
- تشير النجوم إلى الرسائل التي تم وضع علامة عليها
- اعرض أسماء المراسلين فوق سطر الموضوع وليس تحته
- استخدم أسماء المستلمين من جهات الاتصال عند توفرها
- Contact name color
- استخدم خطًا ذا عرض ثابت عند إظهار رسائل نصية عادية
- إظهار مربع حوار كلما قمت بتنفيذ الإجراءات المحددة
-
- Swipe actions
-
- Right swipe
-
- Left swipe
-
- تبديل التحديد
-
- وضع علامة مقروء / غير مقروء
-
- Add/remove star
-
- كثافة
- المدمج
- استرخاء
- استخدم التوقيت العالمي المنسق (UTC) بدلاً من المنطقة الزمنية المحلية في رؤوس البريد ورأس الرد
- تعطيل الرنين والطنين والوميض في الليل
- لاستخدام حساب البريد الإلكتروني هذا مع K-9 Mail ، تحتاج إلى تسجيل الدخول ومنح التطبيق حق الوصول إلى رسائل البريد الإلكتروني الخاصة بك.
-
- تسجيل الدخول
-
- تسجيل الدخول بأستخدام جوجل
-
- لعرض كلمة المرور الخاصة بك هنا ، قم بتمكين قفل الشاشة على هذا الجهاز.
- تحقق من هويتك
- افتح القفل لعرض كلمة المرور الخاصة بك
- استرداد معلومات الحساب \ u2026
- التحقق من إعدادات الخادم الوارد \ u2026
- التحقق من إعدادات خادم الصادر…
- إحضار إعدادات الحساب …
- OAuth 2.0
- استخدم الضغط
- أرسل معرّف العميل
- اكتشاف مساحة اسم IMAP تلقائيًا
- إظهار المجلدات المشتركة فقط
- إعداد غير صالح: %s
- تردد استطلاع المجلد
- تحديث اتصال IDLE
- لا يمكن نسخ أو نقل رسالة غير متزامنة مع الخادم
- قدم الخادم شهادة SSL غير صالحة. في بعض الأحيان ، يكون هذا بسبب خطأ في تهيئة الخادم. في بعض الأحيان يكون ذلك بسبب محاولة شخص ما مهاجمتك أو مهاجمة خادم البريد الخاص بك. إذا لم تكن متأكدًا من الأمر ، فانقر فوق رفض واتصل بالأشخاص الذين يديرون خادم البريد الخاص بك. \ n \ n (%s )
- لا يمكن الاتصال بالسيرفر. \ n (%s )
- تم إلغاء التفويض
- فشل التفويض مع الخطأ التالي: %s
- OAuth 2.0 غير مدعوم حاليًا مع هذا الموفر.
- تعذر على التطبيق العثور على متصفح لاستخدامه لمنح حق الوصول إلى حسابك
- إعلام في شريط الحالة أثناء فحص البريد
- تجاهل رسائل الدردشة
- لا تعرض إشعارات للرسائل التي تنتمي إلى محادثة عبر البريد الإلكتروني
- وضع علامة كمقروءة عند الحذف
- وضع علامة على رسالة كمقروءة عند حذفها
- فئات الإشعارات
- تكوين الإخطارات للرسائل الجديدة
- تكوين إخطارات الخطأ والحالة
- اعرض دائمًا نسخة إلى / نسخة مخفية الوجهة
- تحميل الرسائل المرسلة
- تحميل الرسائل إلى مجلد المرسل بعد الإرسال
- تكوين مفتاح من طرف إلى طرف
- لم يتم تكوين تطبيق OpenPGP
- تخزين جميع المسودات مشفرة
- سيتم تخزين جميع المسودات مشفرة
- تشفير المسودات فقط إذا تم تمكين التشفير
- تردد استطلاع المجلد
- لون التمييز لهذا الحساب المستخدم في قائمة المجلدات والحسابات
- إحضار الرسائل حتى
- دفع المجلدات
- نقل / نسخ مجلدات الوجهة
- مزامنة عمليات حذف الخادم
- تطبيق OpenPGP مفقود - هل تم إلغاء تثبيته؟
- اعرض بالقرب من أعلى قائمة المجلدات
- Folder poll class
- نفس فئة العرض
- فئة دفع المجلد
- نفس فئة الاستطلاع
- نفس فئة الدفع
- اهتزاز
- معطل
- ضوء الإشعارات
- معطل
- اللون الافتراضي للنظام
- أبيض
- أحمر
- أخضر
- أزرق
- أصفر
- ازرق سماوي
- أرجواني
- تعيين الافتراضي الخاص بك من ، مخفية الوجهة والتوقيع
- قم بإعداد عناوين وتوقيعات بديلة في \"من\"
- جميع الرسائل المخفية إلى
- الانتقال إلى الأعلى / جعله افتراضيًا
- المرسل عكس الترتيب الأبجدي
- وصول
- استخدم النظام الافتراضي
- موضوع عرض الرسالة
- لم يتم العثور على إعدادات
- موضوع الرسالة الثابتة
- حدد موضوع عرض الرسالة أثناء عرض الرسالة
- استخدم سمة عرض رسالة ثابتة
- عند تحديد \"المزامنة التلقائية\"
- أقصى عدد من المجلدات للتحقق مع الدفع
- استخدم تأثيرات بصرية مبهرجة
- إظهار علبة الوارد الموحدة
- إظهار العدد المميّز بنجمة
- يتم عرض جميع الرسائل في صندوق الوارد الموحد
- للعرض
- تلقائي (%s)
- المستلمون
- لم يتم العثور على تطبيق مناسب لهذا الإجراء.
- حفظ أو تجاهل التغييرات؟
- مسح الرسائل المحلية؟
- سيؤدي هذا إلى إزالة جميع الرسائل المحلية من المجلد. لن يتم حذف أي رسائل من الخادم.
- رسائل واضحة
- تأكيد وضع علامة على الكل كمقروء
- اختيار ملف
- تم استيراد الإعدادات بنجاح
-
- الرجاء إدخال كلمات المرور
-
- الرجاء تسجيل الدخول
-
- الرجاء تسجيل الدخول وإدخال كلمات المرور
- فشل استيراد الإعدادات
- فشلت قراءة ملف الإعدادات
- فشل استيراد بعض الإعدادات
- تم الاستيراد بنجاح
- كلمة المرور مطلوبة
-
- تسجيل الدخول مطلوب
- غير مستورد
- فشل الاستيراد
- كلمة مرور الخادم الواردة
- كلمة مرور خادم البريد الصادر
- استخدم نفس كلمة المرور لخادم البريد الصادر
- إظهار العدد غير المقروء لـ …
- الحساب الذي يجب عرض العدد غير المقروء له
- عرض العدد غير المقروء لمجلد واحد فقط
- المجلد الذي يجب عرض العدد غير المقروء له
- %1$s - %2$s
- انسخ نص الارتباط إلى الحافظة
- نص الارتباط
- حد بحث الخادم
- فشل البحث عن بعد
- مطلوب اتصال شبكة للبحث عن الخادم.
- ستظهر خلفية مختلفة أن الرسالة قد تمت قراءتها
- عرض مترابطة
- عرض تقسيم الشاشة
- عندما تكون في الاتجاه الأفقي
- الرجاء تحديد رسالة على اليسار
- إظهار صور جهات الاتصال في قائمة الرسائل
- إجراءات الرسائل المرئية
- إظهار الإجراءات المحددة في قائمة عرض الرسالة
- غير قادر على مصادقة. لا يعلن الخادم عن إمكانية SASL EXTERNAL. قد يكون هذا بسبب مشكلة في شهادة العميل (منتهية الصلاحية ، مرجع مصدق غير معروف) أو مشكلة تكوين أخرى.
-
- إزالة اختيار شهادة العميل
- فشل استرداد شهادة العميل للاسم المستعار \"%s \"
- شهادة العميل \"%1$s \" منتهية الصلاحية أو غير صالحة بعد (%2$s )
-
- إضافة من جهات الاتصال
- BCC
- الرد على
- <مستلم غير معروف>
- <مرسل غير معروف>
- لم يتم تكوين مجلد مسودات لهذا الحساب!
- لم يتم تكوين مفتاح لهذا الحساب! تحقق من الإعدادات الخاصة بك.
- يستخدم موفر التشفير إصدارًا غير متوافق. يرجى التحقق من الإعدادات الخاصة بك!
- لا يمكن الاتصال بموفر التشفير ، تحقق من الإعدادات أو انقر فوق رمز التشفير لإعادة المحاولة!
- فشل في تهيئة التشفير من طرف إلى طرف ، يرجى التحقق من إعداداتك
- لا يدعم وضع PGP / INLINE المرفقات!
- تفعيل PGP / INLINE
- تعطيل PGP / INLINE
- تفعيل تسجيل PGP فقط
- تعطيل تسجيل PGP فقط
- وضع PGP / INLINE
- تم إرسال البريد الإلكتروني بتنسيق PGP / INLINE. \ n يجب استخدام هذا فقط من أجل التوافق:
- يدعم بعض العملاء هذا التنسيق فقط
- التوقيعات قد تنكسر أثناء العبور
- وضع تسجيل PGP فقط
- في هذا الوضع ، سيتم استخدام مفتاح PGP الخاص بك لإنشاء توقيع مشفر لبريد إلكتروني غير مشفر.
- لا يؤدي هذا إلى تشفير البريد الإلكتروني ، ولكنه يتحقق من أنه تم إرساله من مفتاحك.
- قد تنقطع التوقيعات عند إرسالها إلى القوائم البريدية.
- احتوى التوقيع الشامل على خطأ
- يحتوي على توقيع غير معتمد من طرف إلى طرف
- الرسالة مشفرة ولكن بتنسيق غير مدعوم.
- تم تشفير الرسالة ، ولكن تم إلغاء فك التشفير.
- نص عادي موقع من طرف إلى طرف
- لكن المفتاح من طرف إلى طرف لا يتطابق مع المرسل
- لكن مفتاح النهاية إلى النهاية منتهي الصلاحية
- ولكن تم إبطال المفتاح من طرف إلى طرف
- لكن المفتاح من طرف إلى طرف لا يعتبر آمنًا
- من مفتاح طرف إلى طرف غير معروف
- ولكن كان هناك خطأ في فك التشفير
- يجب تنزيل الرسالة بالكامل لفك التشفير
- ولكن لم يتم تكوين تطبيق تشفير
- ولكن ليس من طرف إلى طرف
- تشفير من طرف إلى طرف
- من مرسل تم التحقق منه
- من مفتاح طرف إلى طرف غير معروف
- لكن المفتاح من طرف إلى طرف لا يتطابق مع المرسل
- لكن مفتاح النهاية إلى النهاية منتهي الصلاحية
- ولكن تم إبطال المفتاح من طرف إلى طرف
- لكن المفتاح من طرف إلى طرف لا يعتبر آمنًا
- لكن البيانات الشاملة بها أخطاء
- لكن التشفير لا يعتبر آمنًا
- لم يتم تشفير هذا الجزء ، وربما يكون غير آمن.
- يجب تنزيل الرسالة المشفرة لفك التشفير.
- الشخصيات الخاصة غير مدعومة حاليًا!
- خطأ في تحليل العنوان!
- إخفاء التوقيعات غير المشفرة
- سيتم عرض التوقيعات المشفرة فقط
- سيتم عرض جميع التوقيعات
- التشفير غير متوفر في وضع التسجيل فقط!
- تم تشفير هذا البريد الإلكتروني باستخدام OpenPGP. \ n لقراءته ، تحتاج إلى تثبيت وتكوين تطبيق OpenPGP متوافق.
- اذهب للاعدادات
-
- بعض المستلمين المحددين لا يدعمون هذه الميزة!
- يضمن تشفير الرسائل إمكانية قراءتها من قبل المستلم ولا يمكن لأي شخص آخر قراءتها.
- سيظهر التشفير فقط إذا كان يدعمه جميع المستلمين ، ويجب أن يكونوا قد أرسلوا إليك بريدًا إلكترونيًا من قبل.
- تبديل التشفير عن طريق النقر فوق هذا الرمز.
- وضع التشفير التلقائي المتبادل
- وضع التشفير التلقائي المتبادل
- عادةً ما يتم تشفير الرسائل عن طريق الاختيار ، أو عند الرد على رسالة مشفرة.
- إذا قام كل من المرسل والمستلم بتمكين الوضع المتبادل ، فسيتم تمكين التشفير افتراضيًا.
- يتطلب K-9 Mail OpenKeychain للتشفير من طرف إلى طرف.
- تشفير مواضيع الرسالة
- قد لا تكون مدعومة من قبل بعض المستلمين
- خطأ داخلي: حساب غير صالح!
- خطأ في الاتصال بـ %s!
- إرسال رسالة إعداد تشفير تلقائي
- مشاركة الإعداد الشامل بأمان مع الأجهزة الأخرى
- تشفير تلقائي لرسالة الإعداد
- تقوم رسالة إعداد Autocrypt بمشاركة الإعداد الشامل الخاص بك بأمان مع الأجهزة الأخرى.
- إرسال رسالة الإعداد
- سيتم إرسال الرسالة إلى عنوانك:
- جاري إنشاء رسالة الإعداد …
- للإنهاء ، افتح الرسالة على جهازك الآخر وأدخل رمز الإعداد.
- إظهار كود الإعداد
- تشفير تلقائي لرسالة الإعداد
- تحتوي هذه الرسالة على جميع المعلومات اللازمة لنقل إعدادات التشفير التلقائي إلى جانب مفتاحك السري بأمان من جهازك الأصلي. لإعداد جهازك الجديد لـ Autocrypt ، يرجى اتباع الإرشادات التي يجب أن يقدمها جهازك الجديد. يمكنك الاحتفاظ هذه الرسالة واستخدامها كنسخة احتياطية لمفتاحك السري. إذا كنت ترغب في القيام بذلك ، يجب عليك كتابة كلمة المرور وتخزينها بشكل آمن
- حدث خطأ أثناء إرسال الرسالة. يرجى التحقق من اتصال الشبكة وتكوين خادم البريد الصادر.
-
- السماح بالوصول إلى جهات الاتصال
- لتتمكن من تقديم اقتراحات جهات الاتصال وعرض أسماء جهات الاتصال والصور ، يحتاج التطبيق إلى الوصول إلى جهات الاتصال الخاصة بك.
- حدث خطأ أثناء تحميل البيانات
- جار تهيئة…
- في انتظار رسائل البريد الإلكتروني الجديدة
- السكون حتى يتم السماح بالمزامنة في الخلفية
- السكون حتى تتوفر الشبكة
- انقر لمعرفة المزيد.
- دفع المعلومات
- عند استخدام Push ، يحتفظ K-9 Mail بالاتصال بخادم البريد. يتطلب Android عرض إشعار مستمر عندما يكون التطبيق نشطًا في الخلفية. %s
- ومع ذلك ، يسمح لك Android أيضًا بإخفاء الإشعار.
- تكوين الإشعارات
- إذا لم تكن بحاجة إلى إشعارات فورية حول الرسائل الجديدة ، فيجب عليك تعطيل ميزة Push and Use Polling. يتحقق الاقتراع من البريد الجديد على فترات منتظمة ولا يحتاج إلى الإشعارات.
- تعطيل الدفع
-
- علامة القراءة
-
- يتحرك…
-
- إظهار زر الإنشاء العائم
-
- خطأ
-
- المجلد غير موجود
-
- تفاصيل الرسالة
-
- رأس \"التاريخ\" مفقود
-
- الرد على
-
- BCC
-
- حدث خطأ أثناء تحميل تفاصيل الرسالة.
-
- أضف إلى جهات الاتصال
-
- إنشاء رسالة إلى
-
- انسخ عنوان البريد الإلكتروني
-
- انسخ الاسم وعنوان البريد الإلكتروني
-
- الاسم وعنوان البريد الإلكتروني
-
-
\ No newline at end of file
+ الكود المصدري
+ مشروع مفتوح المصدر
+ الموقع الإلكتروني
+ دليل المستخدم
+ الحصول علي مساعدة
+ منتدى المستخدم
+ Fediverse
+ تويتر
+ تعذر تحميل سجل التغيير
+ الإصدار %s
+ إظهار التغييرات الأخيرة عندما تم تحديث التطبيق
+
+ اكتشف الجديد في هذا الإصدار
+
+ عرض
+ نقل إلي
+ نسخ إلي
+ الرسائل الجديدة
+ إلغاء الاشتراك
+ تم نسخ نص الموضوع إلى الحافظة
+ +%1$d المزيد علي %2$s
+ خطأ في الشهادة
+
+ إشعار خطأ
+
+ حدث خطأ أثناء محاولة إنشاء إشعار نظام برسالة جديدة. السبب على الأرجح هو فقدان صوت الإشعار. \ n \ n انقر لفتح إعدادات الإشعارات.
+ تزامن (إرسال)
+ يتم عرضها أثناء انتظار رسائل جديدة
+ سجلات التصدير
+ تم التصدير بنجاح. قد تحتوي السجلات على معلومات حساسة. كن حذرًا لمن ترسلهم إليه.
+ فشل التصدير.
+ يحتوي حقل المستلم على إدخال غير مكتمل!
+
+ إلي %s
+
+ +
+
+ انا
+
+ عرض الصور البعيدة
+ تشير النجوم إلى الرسائل التي تم وضع علامة عليها
+ اعرض أسماء المراسلين فوق سطر الموضوع وليس تحته
+ استخدم أسماء المستلمين من جهات الاتصال عند توفرها
+ Contact name color
+ استخدم خطًا ذا عرض ثابت عند إظهار رسائل نصية عادية
+ إظهار مربع حوار كلما قمت بتنفيذ الإجراءات المحددة
+
+ Swipe actions
+
+ Right swipe
+
+ Left swipe
+
+ تبديل التحديد
+
+ وضع علامة مقروء / غير مقروء
+
+ Add/remove star
+
+ كثافة
+ المدمج
+ استرخاء
+ استخدم التوقيت العالمي المنسق (UTC) بدلاً من المنطقة الزمنية المحلية في رؤوس البريد ورأس الرد
+ تعطيل الرنين والطنين والوميض في الليل
+ لاستخدام حساب البريد الإلكتروني هذا مع K-9 Mail ، تحتاج إلى تسجيل الدخول ومنح التطبيق حق الوصول إلى رسائل البريد الإلكتروني الخاصة بك.
+
+ تسجيل الدخول
+
+ تسجيل الدخول بأستخدام جوجل
+
+ لعرض كلمة المرور الخاصة بك هنا ، قم بتمكين قفل الشاشة على هذا الجهاز.
+ تحقق من هويتك
+ افتح القفل لعرض كلمة المرور الخاصة بك
+ استرداد معلومات الحساب \ u2026
+ التحقق من إعدادات الخادم الوارد \ u2026
+ التحقق من إعدادات خادم الصادر…
+ إحضار إعدادات الحساب …
+ OAuth 2.0
+ استخدم الضغط
+ أرسل معرّف العميل
+ اكتشاف مساحة اسم IMAP تلقائيًا
+ إظهار المجلدات المشتركة فقط
+ إعداد غير صالح: %s
+ تردد استطلاع المجلد
+ تحديث اتصال IDLE
+ لا يمكن نسخ أو نقل رسالة غير متزامنة مع الخادم
+ قدم الخادم شهادة SSL غير صالحة. في بعض الأحيان ، يكون هذا بسبب خطأ في تهيئة الخادم. في بعض الأحيان يكون ذلك بسبب محاولة شخص ما مهاجمتك أو مهاجمة خادم البريد الخاص بك. إذا لم تكن متأكدًا من الأمر ، فانقر فوق رفض واتصل بالأشخاص الذين يديرون خادم البريد الخاص بك. \ n \ n (%s )
+ لا يمكن الاتصال بالسيرفر. \ n (%s )
+ تم إلغاء التفويض
+ فشل التفويض مع الخطأ التالي: %s
+ OAuth 2.0 غير مدعوم حاليًا مع هذا الموفر.
+ تعذر على التطبيق العثور على متصفح لاستخدامه لمنح حق الوصول إلى حسابك
+ إعلام في شريط الحالة أثناء فحص البريد
+ تجاهل رسائل الدردشة
+ لا تعرض إشعارات للرسائل التي تنتمي إلى محادثة عبر البريد الإلكتروني
+ وضع علامة كمقروءة عند الحذف
+ وضع علامة على رسالة كمقروءة عند حذفها
+ فئات الإشعارات
+ تكوين الإخطارات للرسائل الجديدة
+ تكوين إخطارات الخطأ والحالة
+ اعرض دائمًا نسخة إلى / نسخة مخفية الوجهة
+ تحميل الرسائل المرسلة
+ تحميل الرسائل إلى مجلد المرسل بعد الإرسال
+ تكوين مفتاح من طرف إلى طرف
+ لم يتم تكوين تطبيق OpenPGP
+ تخزين جميع المسودات مشفرة
+ سيتم تخزين جميع المسودات مشفرة
+ تشفير المسودات فقط إذا تم تمكين التشفير
+ تردد استطلاع المجلد
+ لون التمييز لهذا الحساب المستخدم في قائمة المجلدات والحسابات
+ إحضار الرسائل حتى
+ دفع المجلدات
+ نقل / نسخ مجلدات الوجهة
+ مزامنة عمليات حذف الخادم
+ تطبيق OpenPGP مفقود - هل تم إلغاء تثبيته؟
+ اعرض بالقرب من أعلى قائمة المجلدات
+ Folder poll class
+ نفس فئة العرض
+ فئة دفع المجلد
+ نفس فئة الاستطلاع
+ نفس فئة الدفع
+ اهتزاز
+ معطل
+ ضوء الإشعارات
+ معطل
+ اللون الافتراضي للنظام
+ أبيض
+ أحمر
+ أخضر
+ أزرق
+ أصفر
+ ازرق سماوي
+ أرجواني
+ تعيين الافتراضي الخاص بك من ، مخفية الوجهة والتوقيع
+ قم بإعداد عناوين وتوقيعات بديلة في \"من\"
+ جميع الرسائل المخفية إلى
+ الانتقال إلى الأعلى / جعله افتراضيًا
+ المرسل عكس الترتيب الأبجدي
+ وصول
+ استخدم النظام الافتراضي
+ موضوع عرض الرسالة
+ لم يتم العثور على إعدادات
+ موضوع الرسالة الثابتة
+ حدد موضوع عرض الرسالة أثناء عرض الرسالة
+ استخدم سمة عرض رسالة ثابتة
+ عند تحديد \"المزامنة التلقائية\"
+ أقصى عدد من المجلدات للتحقق مع الدفع
+ استخدم تأثيرات بصرية مبهرجة
+ إظهار علبة الوارد الموحدة
+ إظهار العدد المميّز بنجمة
+ يتم عرض جميع الرسائل في صندوق الوارد الموحد
+ للعرض
+ تلقائي (%s)
+ المستلمون
+ لم يتم العثور على تطبيق مناسب لهذا الإجراء.
+ حفظ أو تجاهل التغييرات؟
+ مسح الرسائل المحلية؟
+ سيؤدي هذا إلى إزالة جميع الرسائل المحلية من المجلد. لن يتم حذف أي رسائل من الخادم.
+ رسائل واضحة
+ تأكيد وضع علامة على الكل كمقروء
+ اختيار ملف
+ تم استيراد الإعدادات بنجاح
+
+ الرجاء إدخال كلمات المرور
+
+ الرجاء تسجيل الدخول
+
+ الرجاء تسجيل الدخول وإدخال كلمات المرور
+ فشل استيراد الإعدادات
+ فشلت قراءة ملف الإعدادات
+ فشل استيراد بعض الإعدادات
+ تم الاستيراد بنجاح
+ كلمة المرور مطلوبة
+
+ تسجيل الدخول مطلوب
+ غير مستورد
+ فشل الاستيراد
+ كلمة مرور الخادم الواردة
+ كلمة مرور خادم البريد الصادر
+ استخدم نفس كلمة المرور لخادم البريد الصادر
+ إظهار العدد غير المقروء لـ …
+ الحساب الذي يجب عرض العدد غير المقروء له
+ عرض العدد غير المقروء لمجلد واحد فقط
+ المجلد الذي يجب عرض العدد غير المقروء له
+ %1$s - %2$s
+ انسخ نص الارتباط إلى الحافظة
+ نص الارتباط
+ حد بحث الخادم
+ فشل البحث عن بعد
+ مطلوب اتصال شبكة للبحث عن الخادم.
+ ستظهر خلفية مختلفة أن الرسالة قد تمت قراءتها
+ عرض مترابطة
+ عرض تقسيم الشاشة
+ عندما تكون في الاتجاه الأفقي
+ الرجاء تحديد رسالة على اليسار
+ إظهار صور جهات الاتصال في قائمة الرسائل
+ إجراءات الرسائل المرئية
+ إظهار الإجراءات المحددة في قائمة عرض الرسالة
+ غير قادر على مصادقة. لا يعلن الخادم عن إمكانية SASL EXTERNAL. قد يكون هذا بسبب مشكلة في شهادة العميل (منتهية الصلاحية ، مرجع مصدق غير معروف) أو مشكلة تكوين أخرى.
+
+ إزالة اختيار شهادة العميل
+ فشل استرداد شهادة العميل للاسم المستعار \"%s \"
+ شهادة العميل \"%1$s \" منتهية الصلاحية أو غير صالحة بعد (%2$s )
+
+ إضافة من جهات الاتصال
+ BCC
+ الرد على
+ <مستلم غير معروف>
+ <مرسل غير معروف>
+ لم يتم تكوين مجلد مسودات لهذا الحساب!
+ لم يتم تكوين مفتاح لهذا الحساب! تحقق من الإعدادات الخاصة بك.
+ يستخدم موفر التشفير إصدارًا غير متوافق. يرجى التحقق من الإعدادات الخاصة بك!
+ لا يمكن الاتصال بموفر التشفير ، تحقق من الإعدادات أو انقر فوق رمز التشفير لإعادة المحاولة!
+ فشل في تهيئة التشفير من طرف إلى طرف ، يرجى التحقق من إعداداتك
+ لا يدعم وضع PGP / INLINE المرفقات!
+ تفعيل PGP / INLINE
+ تعطيل PGP / INLINE
+ تفعيل تسجيل PGP فقط
+ تعطيل تسجيل PGP فقط
+ وضع PGP / INLINE
+ تم إرسال البريد الإلكتروني بتنسيق PGP / INLINE. \ n يجب استخدام هذا فقط من أجل التوافق:
+ يدعم بعض العملاء هذا التنسيق فقط
+ التوقيعات قد تنكسر أثناء العبور
+ وضع تسجيل PGP فقط
+ في هذا الوضع ، سيتم استخدام مفتاح PGP الخاص بك لإنشاء توقيع مشفر لبريد إلكتروني غير مشفر.
+ لا يؤدي هذا إلى تشفير البريد الإلكتروني ، ولكنه يتحقق من أنه تم إرساله من مفتاحك.
+ قد تنقطع التوقيعات عند إرسالها إلى القوائم البريدية.
+ احتوى التوقيع الشامل على خطأ
+ يحتوي على توقيع غير معتمد من طرف إلى طرف
+ الرسالة مشفرة ولكن بتنسيق غير مدعوم.
+ تم تشفير الرسالة ، ولكن تم إلغاء فك التشفير.
+ نص عادي موقع من طرف إلى طرف
+ لكن المفتاح من طرف إلى طرف لا يتطابق مع المرسل
+ لكن مفتاح النهاية إلى النهاية منتهي الصلاحية
+ ولكن تم إبطال المفتاح من طرف إلى طرف
+ لكن المفتاح من طرف إلى طرف لا يعتبر آمنًا
+ من مفتاح طرف إلى طرف غير معروف
+ ولكن كان هناك خطأ في فك التشفير
+ يجب تنزيل الرسالة بالكامل لفك التشفير
+ ولكن لم يتم تكوين تطبيق تشفير
+ ولكن ليس من طرف إلى طرف
+ تشفير من طرف إلى طرف
+ من مرسل تم التحقق منه
+ من مفتاح طرف إلى طرف غير معروف
+ لكن المفتاح من طرف إلى طرف لا يتطابق مع المرسل
+ لكن مفتاح النهاية إلى النهاية منتهي الصلاحية
+ ولكن تم إبطال المفتاح من طرف إلى طرف
+ لكن المفتاح من طرف إلى طرف لا يعتبر آمنًا
+ لكن البيانات الشاملة بها أخطاء
+ لكن التشفير لا يعتبر آمنًا
+ لم يتم تشفير هذا الجزء ، وربما يكون غير آمن.
+ يجب تنزيل الرسالة المشفرة لفك التشفير.
+ الشخصيات الخاصة غير مدعومة حاليًا!
+ خطأ في تحليل العنوان!
+ إخفاء التوقيعات غير المشفرة
+ سيتم عرض التوقيعات المشفرة فقط
+ سيتم عرض جميع التوقيعات
+ التشفير غير متوفر في وضع التسجيل فقط!
+ تم تشفير هذا البريد الإلكتروني باستخدام OpenPGP. \ n لقراءته ، تحتاج إلى تثبيت وتكوين تطبيق OpenPGP متوافق.
+ اذهب للاعدادات
+
+ بعض المستلمين المحددين لا يدعمون هذه الميزة!
+ يضمن تشفير الرسائل إمكانية قراءتها من قبل المستلم ولا يمكن لأي شخص آخر قراءتها.
+ سيظهر التشفير فقط إذا كان يدعمه جميع المستلمين ، ويجب أن يكونوا قد أرسلوا إليك بريدًا إلكترونيًا من قبل.
+ تبديل التشفير عن طريق النقر فوق هذا الرمز.
+ وضع التشفير التلقائي المتبادل
+ وضع التشفير التلقائي المتبادل
+ عادةً ما يتم تشفير الرسائل عن طريق الاختيار ، أو عند الرد على رسالة مشفرة.
+ إذا قام كل من المرسل والمستلم بتمكين الوضع المتبادل ، فسيتم تمكين التشفير افتراضيًا.
+ يتطلب K-9 Mail OpenKeychain للتشفير من طرف إلى طرف.
+ تشفير مواضيع الرسالة
+ قد لا تكون مدعومة من قبل بعض المستلمين
+ خطأ داخلي: حساب غير صالح!
+ خطأ في الاتصال بـ %s!
+ إرسال رسالة إعداد تشفير تلقائي
+ مشاركة الإعداد الشامل بأمان مع الأجهزة الأخرى
+ تشفير تلقائي لرسالة الإعداد
+ تقوم رسالة إعداد Autocrypt بمشاركة الإعداد الشامل الخاص بك بأمان مع الأجهزة الأخرى.
+ إرسال رسالة الإعداد
+ سيتم إرسال الرسالة إلى عنوانك:
+ جاري إنشاء رسالة الإعداد …
+ للإنهاء ، افتح الرسالة على جهازك الآخر وأدخل رمز الإعداد.
+ إظهار كود الإعداد
+ تشفير تلقائي لرسالة الإعداد
+ تحتوي هذه الرسالة على جميع المعلومات اللازمة لنقل إعدادات التشفير التلقائي إلى جانب مفتاحك السري بأمان من جهازك الأصلي. لإعداد جهازك الجديد لـ Autocrypt ، يرجى اتباع الإرشادات التي يجب أن يقدمها جهازك الجديد. يمكنك الاحتفاظ هذه الرسالة واستخدامها كنسخة احتياطية لمفتاحك السري. إذا كنت ترغب في القيام بذلك ، يجب عليك كتابة كلمة المرور وتخزينها بشكل آمن
+ حدث خطأ أثناء إرسال الرسالة. يرجى التحقق من اتصال الشبكة وتكوين خادم البريد الصادر.
+
+ السماح بالوصول إلى جهات الاتصال
+ لتتمكن من تقديم اقتراحات جهات الاتصال وعرض أسماء جهات الاتصال والصور ، يحتاج التطبيق إلى الوصول إلى جهات الاتصال الخاصة بك.
+ حدث خطأ أثناء تحميل البيانات
+ جار تهيئة…
+ في انتظار رسائل البريد الإلكتروني الجديدة
+ السكون حتى يتم السماح بالمزامنة في الخلفية
+ السكون حتى تتوفر الشبكة
+ انقر لمعرفة المزيد.
+ دفع المعلومات
+ عند استخدام Push ، يحتفظ K-9 Mail بالاتصال بخادم البريد. يتطلب Android عرض إشعار مستمر عندما يكون التطبيق نشطًا في الخلفية. %s
+ ومع ذلك ، يسمح لك Android أيضًا بإخفاء الإشعار.
+ تكوين الإشعارات
+ إذا لم تكن بحاجة إلى إشعارات فورية حول الرسائل الجديدة ، فيجب عليك تعطيل ميزة Push and Use Polling. يتحقق الاقتراع من البريد الجديد على فترات منتظمة ولا يحتاج إلى الإشعارات.
+ تعطيل الدفع
+
+ علامة القراءة
+
+ يتحرك…
+
+ إظهار زر الإنشاء العائم
+
+ خطأ
+
+ المجلد غير موجود
+
+ تفاصيل الرسالة
+
+ رأس \"التاريخ\" مفقود
+
+ الرد على
+
+ BCC
+
+ حدث خطأ أثناء تحميل تفاصيل الرسالة.
+
+ أضف إلى جهات الاتصال
+
+ إنشاء رسالة إلى
+
+ انسخ عنوان البريد الإلكتروني
+
+ انسخ الاسم وعنوان البريد الإلكتروني
+
+ الاسم وعنوان البريد الإلكتروني
+
+
+
diff --git a/app/ui/legacy/src/main/res/values-be/strings.xml b/app/ui/legacy/src/main/res/values-be/strings.xml
index 918729fd04b94aa00d0eb7528f9d94f4b0e98e59..b9a27cf6c56fcc7667e83eb31ba9ffd6338366e5 100644
--- a/app/ui/legacy/src/main/res/values-be/strings.xml
+++ b/app/ui/legacy/src/main/res/values-be/strings.xml
@@ -28,9 +28,6 @@
Даведайцеся, што новага ў гэтым выпуску
Праглядзець
-
- Вітаем у Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -204,7 +201,6 @@
Схаваная копія:
Немагчыма захаваць далучаныя файлы.
- "да "
+
@@ -311,9 +307,6 @@
Аўтэнтыфікацыя\u2026
Атрыманне налад акаўнта\u2026
Скасаванне\u2026
- Амаль завершана!
- Даць акаўнту назву (неабавязкова):
- Ваша імя (бачна адрасату):
Тып акаўнта
Што гэта за акаўнт?
POP3
@@ -364,7 +357,6 @@
Аўтэнтыфікацыя
\"%1$s = %2$s \" непрыдатны для \"%3$s = %4$s \"
Хібнае наладжванне: %s
- Параметры акаўнта
Частата апытання
Ніколі
Кожныя 15 хвілін
@@ -384,7 +376,6 @@
Кожныя 36 хвілін
Кожныя 48 хвілін
Кожныя 60 хвілін
- Апавяшчаць пра новую пошту
Колькасць лістоў для паказу
10 лістоў
25 лістоў
@@ -1106,4 +1097,7 @@
Імя і email адрас
+
+ да %s
+
diff --git a/app/ui/legacy/src/main/res/values-bg/strings.xml b/app/ui/legacy/src/main/res/values-bg/strings.xml
index 8e6e6939f26145d0943ce2bc0803faf275eae2bb..4438f2a9548d5998fe0c8161b5ae614f0d912006 100644
--- a/app/ui/legacy/src/main/res/values-bg/strings.xml
+++ b/app/ui/legacy/src/main/res/values-bg/strings.xml
@@ -24,9 +24,6 @@
Разбери какво ново в тази варсия
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -275,9 +272,6 @@
Идентифициране\u2026
Получава настройките на профила\u2026
Отказва\u2026
- Почти проключихте!
- Дайте име на профила (незадължително):
- Въведете вашето име (ще се показва в изходящите съобщения):
Тип на профила
Какъв тип профил е това?
POP3
@@ -326,7 +320,6 @@
Идентификация
\"%1$s = %2$s \" не е валидно с \"%3$s = %4$s \"
Невалидни настройки: %s
- Настройки на профила
Честота на проверка на папката
Никога
На всеки 15 минути
@@ -346,7 +339,6 @@
На всеки 36 минути
На всеки 48 минути
На всеки 60 минути
- Уведоми ме когато пристигне поща
Брой съобщения за показване
10 съобщения
25 съобщения
@@ -930,7 +922,7 @@
Autocrypt съобщение с настройки сигурно споделя от край-до-край вашите настройките с други устройства.
Изпрати съобщение с настройки
Съобщението ще бъде изпратено до следния адрес:
- Създаване на съобщение с настройки...
+ Създаване на съобщение с настройки…
Изпращане на съобщение до:
За да завършите, отворете съобщението на друго устройство и въведете кодът за достъп.
Показване на код за достъп
@@ -948,7 +940,7 @@
Позволи достъп до контакти
За да може да предоставя предложения за контакти и да показва имена на контакти и снимки, приложението се нуждае от достъп до вашите контакти.
Възникна грешка при зареждането на данните
- Инициализира...
+ Инициализира…
Изчаква за нови съобщения
Заспива докато синхронизирането на заден фон е разрешено
Спи докато чака за мрежа
@@ -1002,6 +994,6 @@
Имейл адрес
-
+
diff --git a/app/ui/legacy/src/main/res/values-br/strings.xml b/app/ui/legacy/src/main/res/values-br/strings.xml
index f55cd2e10eca3f1516e16d716ecd673a4687fa6d..cea7208e8bbd6640255b14a98762c0a1a6fc4724 100644
--- a/app/ui/legacy/src/main/res/values-br/strings.xml
+++ b/app/ui/legacy/src/main/res/values-br/strings.xml
@@ -13,9 +13,6 @@
Lañvaz
-
- Donemat war Mail
- Mail is the default mail client for /e/
-- \nKaset eus ma fellgomzer Android gant Mail.
@@ -258,9 +255,6 @@
Dilesa\u2026
O kerc’hat arventennoù ar gont\u2026
O nullañ\u2026
- Tost echu!
- Roit un anv d’ar gont-mañ (diret):
- Skrivit hoc’h anv (diskouezet e vo er c’hemennadennoù kaset):
Doare kont
Peseurt doare kont eo?
POP3
@@ -309,7 +303,6 @@
Dilesa
%1$s = %2$s \" n’eo ket talvoudek gant \"%3$s = %4$s
Arventennoù didalvoudek: %s
- Dibarzhioù ar gont
Aliested kerc’hat ar posteloù
Morse
Bep 15 munutenn
@@ -329,7 +322,6 @@
Bep 36 munutenn
Bep 48 munutenn
Bep 60 munutenn
- Ma rebuziñ pa zegouezh ar posteloù
Niver a gemennadennoù da ziskouez
10 kemennadenn
25 kemennadenn
@@ -951,4 +943,5 @@ Gallout a rit mirout ar gemennadenn-mañ hag implij anezhi evel un enrolladenn e
Chomlec’h postel
+
diff --git a/app/ui/legacy/src/main/res/values-ca/strings.xml b/app/ui/legacy/src/main/res/values-ca/strings.xml
index 3f1c3fd0cf853d76e30e4753ca0326c0fb840f23..4fe3c47c8961d890409fe6848727e554d321bac9 100644
--- a/app/ui/legacy/src/main/res/values-ca/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ca/strings.xml
@@ -28,9 +28,6 @@
Esbrineu les novetats d’aquesta versió.
Vista
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -197,8 +194,6 @@
A/c:
Bcc:
No s\'ha pogut desar l\'adjunt.
-
- "a "
+
@@ -306,9 +301,6 @@
Autenticant\u2026
Recuperant la configuració del compte\u2026
Cancel·lant\u2026
- Ja gairebé hem acabat!
- Dóna un nom a aquest compte (opcional):
- Escriviu el nom (és el que es mostrarà als missatges sortints):
Tipus de compte
Quin tipus de compte és?
POP3
@@ -360,7 +352,6 @@
Tipus d\'autenticació
\"%1$s = %2$s \" no és vàlid amb \"%3$s = %4$s \"
Configuració no vàlida: %s
- Opcions del compte
Freqüència de comprovació
Mai
Cada 15 minuts
@@ -380,7 +371,6 @@
Cada 36 minuts
Cada 48 minuts
Cada 60 minuts
- Avisa\'m quan arribi correu
Nombre de missatges a mostrar
10 missatges
25 missatges
@@ -1018,6 +1008,10 @@ Per configurar el vostre dispositiu nou per a l\'autoencriptació, si us plau, s
Podeu guardar aquest missatge i usar-lo com a còpia de seguretat per a la vostra clau secreta. Si ho voleu fer, hauríeu d\'anotar-vos la contrasenya i desar-la amb seguretat.
+ Mou a…
+ Copia a…
+
+ a %s
Hi ha hagut un error mentre s\'enviava el missatge. Si us plau, comproveu la connexió de xarxa i la configuració del servidor de sortida.
Actiu
Inactiu
@@ -1094,4 +1088,7 @@ Podeu guardar aquest missatge i usar-lo com a còpia de seguretat per a la vostr
Nom i adreça de correu
+ Se suprimeix el compte…
+
+ sense llegir, %s
diff --git a/app/ui/legacy/src/main/res/values-cs/strings.xml b/app/ui/legacy/src/main/res/values-cs/strings.xml
index 2becf5d8c383e0c1cd554d860791f5c4a4e196a0..021f1a49f580bd507912791c2eea46f38d991ebc 100644
--- a/app/ui/legacy/src/main/res/values-cs/strings.xml
+++ b/app/ui/legacy/src/main/res/values-cs/strings.xml
@@ -28,9 +28,6 @@
Zjistěte, co je v tomto vydání nového
Zobrazit
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -203,8 +200,6 @@
Kopie:
Skrytá kopie (Bcc):
Přílohu se nedaří uložit.
-
- "komu "
+
@@ -311,9 +306,6 @@
Přihlašuji se\u2026
Stahuji nastavení účtu\u2026
Rušení\u2026
- Téměř hotovo!
- Pojmenujte tento účet (volitelné):
- Zadejte své jméno (zobrazuje se v odchozích zprávách):
Typ účtu
O jaký typ účtu jde?
POP3
@@ -364,7 +356,6 @@
Typ ověření
„%1$s = %2$s “ není platný s „%3$s = %4$s “
Neplatné nastavení: %s
- Možnosti účtu
Frekvence dotazování složky
Nikdy
Každých 15 minut
@@ -384,7 +375,6 @@
Každých 36 minut
Každých 48 minut
Každých 60 minut
- Oznamuj mi příchod nové pošty
Počet zobrazených zpráv
10 zpráv
25 zpráv
@@ -1032,6 +1022,8 @@
Pro nastavení nového zařízení pro Autocrypt, postupujte podle pokynů které by se měly zobrazit na novém zařízení.
Tuto zprávu si můžete ponechat a použít jí jako zálohu svého tajného klíče. Pokud toho chcete využít, měli byste si zapsat heslo a bezpečně ho uložit.
+
+ komu %s
Při odesílání zprávy došlo k chybě. Zkontrolujte své připojení k síti a nastavení odchozího serveru.
Zapnuto
Vypnuto
@@ -1108,4 +1100,5 @@ Tuto zprávu si můžete ponechat a použít jí jako zálohu svého tajného kl
Jméno a adresa
+
diff --git a/app/ui/legacy/src/main/res/values-cy/strings.xml b/app/ui/legacy/src/main/res/values-cy/strings.xml
index dae954dee01012d721babe5d30c569363926168e..6c0fa9d2d76f0dfcc46566dc3a4cb03b2153dd22 100644
--- a/app/ui/legacy/src/main/res/values-cy/strings.xml
+++ b/app/ui/legacy/src/main/res/values-cy/strings.xml
@@ -27,9 +27,6 @@
Darganfod beth sy\'n newydd yn y diweddariad hwn
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -297,9 +294,6 @@
Yn dilysu\u2026
Yn nôl gosodiadau\'r cyfrif\u2026
Yn diddymu\u2026
- Bron a gorffen!
- Rhoi enw i\'r cyfrif hwn (dim yn ofynnol):
- Dy enw (i\'w weld ar negeseuon sy\'n cael eu hanfon):
Math o gyfrif
Pa fath o gyfrif ydy hwn?
POP3
@@ -350,7 +344,6 @@
Dilysiad
Dyw \"%1$s = %2$s \" ddim yn dilys gyda \"%3$s = %4$s \"
Gosodiad annilys: %s
- Dewisiadau cyfrif
Pa mor aml i wirio\'r ffolder
Byth
Pob 15 munud
@@ -370,7 +363,6 @@
Pob 36 munud
Pob 48 munud
Pob 60 munud
- Hysbysu pan ddaw neges newydd
Nifer o negeseuon i\'w dangos
10 neges
25 neges
@@ -1081,4 +1073,5 @@ Mae\'n bosib i ti gadw\'r neges hon a\'i ddefnyddio wrth gefn fel dy allwedd gyf
Cyfeiriad e-bost
+
diff --git a/app/ui/legacy/src/main/res/values-da/strings.xml b/app/ui/legacy/src/main/res/values-da/strings.xml
index 99ec98bf8dfbfdd88ab9b522b0908d9af3efc06c..54665a5d92539e61cc95c6b420a65a3a75aaaf10 100644
--- a/app/ui/legacy/src/main/res/values-da/strings.xml
+++ b/app/ui/legacy/src/main/res/values-da/strings.xml
@@ -27,9 +27,6 @@
Find ud af hvad der er nyt i denne version
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -195,8 +192,6 @@
Cc:
Bcc:
Kunne ikke gemme vedhæftning.
-
- "til "
+
@@ -297,9 +292,6 @@
Autentiserer\u2026
Henter kontoindstillinger\u2026
Afbryder\u2026
- Næsten færdig!
- Giv denne konto et navn (frivilligt):
- Indtast dit navn (vises på udgående mails):
Kontotype
Hvilken type konto er dette?
POP3
@@ -350,7 +342,6 @@
Autentifikationstype
\"%1$s = %2$s \" er ikke gyldig med \"%3$s = %4$s \"
Ugyldig opsætning: %s
- Kontoindstillinger
Frekvens for hentning af mail
Aldrig
Hver 15 minut
@@ -370,7 +361,6 @@
Hver 36 minut
Hver 48 minut
Hver 60 minut
- Vis besked når mails ankommer
Antal viste mails
10 mails
25 mails
@@ -997,6 +987,12 @@ For at indstille den nye enhed til Autocrypt, følg instruktionerne der vises p
Beskeden kan beholdes og bruges som sikkerhedskopi til den hemmelige nøgle. Hvis dette gøres skal adgangskoden gemmes sikkert.
+
+ til %s
+
+ Tæthed
+ Konfiguér notifikationer for nye beskeder
+ Konfiguér fejl og status notifikationer
Vis
Afmeld
@@ -1081,4 +1077,5 @@ Beskeden kan beholdes og bruges som sikkerhedskopi til den hemmelige nøgle. Hvi
Skriv meddelelse til
+
diff --git a/app/ui/legacy/src/main/res/values-de/strings.xml b/app/ui/legacy/src/main/res/values-de/strings.xml
index 5fa877d2340e2f48554da622e1b0f04657e39611..2938d4518249170c45f8a66c223994945dfee505 100644
--- a/app/ui/legacy/src/main/res/values-de/strings.xml
+++ b/app/ui/legacy/src/main/res/values-de/strings.xml
@@ -25,9 +25,6 @@
Was gibt es Neues
Zeige die neuesten Änderungen nach einem App-Update
Finde heraus, was es Neues in diesem Release gibt
-
- Willkommen bei Mail
- E-Mail ist die Standard-E-Mailanwendung für /e/OS
-- Von /e/OS E-Mail gesendet.
@@ -262,9 +259,6 @@
Authentifizierung\u2026
Kontoeinstellungen werden geladen\u2026
Aktion wird abgebrochen\u2026
- Fast fertig!
- Kontoname (optional):
- Ihr Name (Anzeige bei ausgehenden Nachrichten):
Kontotyp
Art des Kontos?
POP3-Konto
@@ -315,7 +309,6 @@
Authentifizierungsmethode
\"%1$s = %2$s \" ist nicht gültig mit \"%3$s = %4$s \"
Ungültige Einstellung: %s
- Kontooptionen
E-Mail-Abfrage
Nie (nur manuell)
Alle 15 Minuten
@@ -335,7 +328,6 @@
Alle 36 Minuten
Alle 48 Minuten
Jede Stunde
- Benachrichtigen, wenn Nachrichten eingehen
Anzahl sichtbarer Nachrichten
10 Nachrichten
25 Nachrichten
@@ -1063,8 +1055,6 @@
Remote-Bilder anzeigen
mich
+
- "an "
- Erstelle ein neues Konto oder importiere deine E-Mail-Konten aus einer vorherigen Einstellungsdatei.
Kompakt
Standard
Entspannt
@@ -1074,6 +1064,9 @@
Kontoname
Nachricht verfassen an
+
+
Konto wird gelöscht …
+
Ungelesen, %s
-
\ No newline at end of file
+
diff --git a/app/ui/legacy/src/main/res/values-el/strings.xml b/app/ui/legacy/src/main/res/values-el/strings.xml
index 5d41fbcc4d01796524f880bd4ad7c2d55c442976..32e9cf5e4c6a7703848afc44c59dd684e75665e4 100644
--- a/app/ui/legacy/src/main/res/values-el/strings.xml
+++ b/app/ui/legacy/src/main/res/values-el/strings.xml
@@ -27,9 +27,6 @@
Δείτε τι έχει αλλάξει σε αυτή την έκδοση
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -294,9 +291,6 @@
Διακρίβωση στοιχείων\u2026
Μετάκληση πληροφοριών λογαριασμού\u2026
Ακύρωση\u2026
- Σχεδόν έτοιμο!
- Ονομασία λογαριασμού (Προαιρετικό):
- Όνομα αποστολέα (προβάλλεται στα εξερχόμενα μηνύματα):
Τύπος λογαριασμού
Τι λογαριασμός είναι αυτός;
POP3
@@ -347,7 +341,6 @@
Πιστοποίηση
Το «%1$s = %2$s » δεν είναι έγκυρο με «%3$s = %4$s »
Άκυρη ρύθμιση: %s
- Επιλογές λογαριασμού
Συχνότητα ενημέρωσης φακέλων
Ποτέ
Κάθε 15 λεπτά
@@ -367,7 +360,6 @@
Κάθε 36 λεπτά
Κάθε 48 λεπτά
Κάθε 60 λεπτά
- Ειδοποίηση κατά την άφιξη μηνύματος
Πλήθος μηνυμάτων που προβάλλονται
10 μηνύματα
25 μηνύματα
@@ -1001,10 +993,14 @@
Μπορείτε να κρατήσετε αυτό το μήνυμα και να το χρησιμοποιήσετε ως εφεδρικό για το μυστικό σας κλειδί. Εάν αυτό επιθυμείτε, θα πρέπει να σημειώσετε τον κωδικό και να τον αποθηκεύσετε σε ασφαλές σημείο.
+
+ σε %s
+
+ εγώ
+ Χαλαρό
+ Αποστολή ID πελάτη
Προβολή
-
- σε
+
@@ -1118,4 +1114,7 @@
\'Ονομα και διεύθυνση ηλ. ταχυδρομείου
+ Αφαίρεση λογαριασμού…
+
+ αδιάβαστα, %s
diff --git a/app/ui/legacy/src/main/res/values-en-rGB/strings.xml b/app/ui/legacy/src/main/res/values-en-rGB/strings.xml
index 2e7d84a1ad383e9f62b712527ec6e7230a9a3642..ddeb370ff8d50b76fb2d319378b9fecdf36a582a 100644
--- a/app/ui/legacy/src/main/res/values-en-rGB/strings.xml
+++ b/app/ui/legacy/src/main/res/values-en-rGB/strings.xml
@@ -9,7 +9,6 @@
-
@@ -17,7 +16,7 @@
-
+
diff --git a/app/ui/legacy/src/main/res/values-eo/strings.xml b/app/ui/legacy/src/main/res/values-eo/strings.xml
index b0d584e0f47023f5dc6ab99d0cc2b6109f83913c..6a3d39a2e60fcdaf39b878f0120c3b73a2212611 100644
--- a/app/ui/legacy/src/main/res/values-eo/strings.xml
+++ b/app/ui/legacy/src/main/res/values-eo/strings.xml
@@ -25,9 +25,6 @@
Kio estas nova en tiu ĉi versio
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -278,9 +275,6 @@
Aŭtentigado\u2026
Ŝargado de agordoj de konto\u2026
Nuligado\u2026
- Preskaŭ farite!
- Nomi tiun ĉi konton (malnepre):
- Entajpu vian nomon (montrata en eliraj mesaĝoj):
Konta tipo
De iu tipo tiu ĉi konto estas?
POP3
@@ -329,7 +323,6 @@
Aŭtentigo
\"%1$s = %2$s \" ne estas ĝusta kun \"%3$s = %4$s \"
Erara agordaro: %s
- Kontaj agordoj
Ofteco de mesaĝuj-elŝuto
Neniam
Je ĉiuj 15 minutoj
@@ -349,7 +342,6 @@
Je ĉiuj 36 minutoj
Je ĉiuj 48 minutoj
Je ĉiuj 60 minutoj
- Sciigi kiam retletero alvenas
Kiom da mesaĝoj montri
10 mesaĝoj
25 mesaĝoj
@@ -1024,4 +1016,5 @@ Vi povas konservi tiun ĉi mesaĝon kaj uzi ĝin kiel sekurkopion de via privata
Retpoŝta adreso
+
diff --git a/app/ui/legacy/src/main/res/values-es/strings.xml b/app/ui/legacy/src/main/res/values-es/strings.xml
index 3817709e234b70a3dd7694542caf170eec9a0f41..12f3cc6e719390c7c31a2a56673d945b28f56b83 100644
--- a/app/ui/legacy/src/main/res/values-es/strings.xml
+++ b/app/ui/legacy/src/main/res/values-es/strings.xml
@@ -27,9 +27,6 @@
Descubra las novedades de esta versión
Leer
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -188,8 +185,6 @@
Cc:
Cco:
No se puede guardar el adjunto.
-
- "para "
+
@@ -297,9 +292,6 @@
Autentificando\u2026
Obteniendo configuración de la cuenta\u2026
Cancelando\u2026
- ¡Terminado!
- Elija un nombre para esta cuenta (opcional):
- Introduzca su nombre (se verá en los mensajes salientes):
Tipo de cuenta
¿Qué tipo de cuenta es?
POP3
@@ -351,7 +343,6 @@
Tipo de autentificación
\"%1$s = %2$s \" no es válido con \"%3$s = %4$s \"
Configuración inválida: %s
- Opciones de cuenta
Frecuencia de comprobación de correo nuevo
Nunca
Cada 15 minutos
@@ -371,7 +362,6 @@
Cada 36 minutos
Cada 48 minutos
Cada 60 minutos
- Avisarme cuando lleguen nuevos mensajes
Número de mensajes a mostrar
10 mensajes
25 mensajes
@@ -1008,6 +998,19 @@ Para configurar su nuevo dispositivo con Autocrypt, por favor siga las instrucci
Puede mantener este mensaje y usarlo como copia de seguridad de su clave secreta. Si desea hacer esto, debería anotar la contraseña y guardarla de manera segura.
+ Mover a…
+ Copiar a…
+ Mensajes nuevos
+ El certificado no funciona
+ Exportar registros de depuración
+ Se han exportado correctamente. Ten en cuenta que los registros pueden contener datos privados; así que ten cuidado y asegúrate de que te fias de las personas a las que se los mandas.
+ No se han podido exportar los registros.
+
+ para %s
+ Categorías de notificaciones
+ Configura las notificaciones para mensajes nuevos
+ Configura las notificaciones de error y estado actual
+ La misma que con sincronización lenta («poll»)
Ha ocurrido un error al enviar el mensaje, por favor verifique su conexión a internet y la configuración del servidor de salida.
Encendido
Apagado
@@ -1084,4 +1087,7 @@ Puede mantener este mensaje y usarlo como copia de seguridad de su clave secreta
Nombre y dirección de correo
Esperando correos electrónicos nuevos
+ Borrando cuenta…
+
+ sin leer, %s
diff --git a/app/ui/legacy/src/main/res/values-et/strings.xml b/app/ui/legacy/src/main/res/values-et/strings.xml
index ef727c236f103a97cc004e77d2dda11d0072a45c..b1f846e7bfc526294001fb3c264ed7d3f5611d90 100644
--- a/app/ui/legacy/src/main/res/values-et/strings.xml
+++ b/app/ui/legacy/src/main/res/values-et/strings.xml
@@ -23,8 +23,6 @@
Vaata mis on selles versioonis muutunud
Vaata
-
- Welcome to Mail
-- Sent from /e/ Mail.
@@ -185,8 +183,6 @@
Koopia:
Pimekoopia:
Manuse salvestamine ebaõnnestus
-
- "saajaks "
+
@@ -294,9 +290,6 @@
Autentimine\u2026
Toob konto sätteid\u2026
Tühistamine\u2026
- Oled peaaegu valmis!
- Anna sellele kontole nimi (valikuline):
- Trüki oma nimi (näidatakse väljuvates kirjades):
Konto liik
Mis tüüpi konto see on?
POP3
@@ -347,7 +340,6 @@
Autentimine
\"%1$s = %2$s \" ei valideeru seadega \"%3$s = %4$s \"
Vigased sätted: %s
- Konto valikud
Kausta pollimise sagedus
Mitte kunagi
Iga 15 minuti tagant
@@ -367,7 +359,6 @@
Iga 36 minuti tagant
Iga 48 minuti tagant
Iga 60 minuti tagant
- Teavita uue e-kirja saabumisest
Kuvatavate kirjade arv
10 kirja
25 kirja
@@ -999,6 +990,15 @@ Seadistamaks uut nutiseadet kasutama Autocrypt\'i palun järgi seal kuvatud juht
Palun jäta see kiri alles ning kasuta seda muu hulgas oma krüptovõtme varundamiseks. Soovitame, et teed seda välises kanalis või andmekandjal ning turvalisel viisil.
+ Kasutusjuhend
+ Abiteave
+ Foorum huvilistele
+ Fediverse
+ Muudatuste logi laadimine ei õnnestunud
+ Teisalda…
+ Kopeeri…
+
+ saajaks %s
Kirja saatmisel tekkis viga. Palun kontrolli võrguühenduse olemasolu ning väljuva e-postiserveri seadistusi.
Sisse
Välja
@@ -1075,4 +1075,7 @@ Palun jäta see kiri alles ning kasuta seda muu hulgas oma krüptovõtme varunda
Nimi ja e-posti aadress
+ Eemaldame kontot…
+
+ lugemata, %s
diff --git a/app/ui/legacy/src/main/res/values-eu/strings.xml b/app/ui/legacy/src/main/res/values-eu/strings.xml
index 5cd089363ce5e8131b38955bfca50f477ef8c9d7..bfb08e5e75e4a2e5e6070ccb2aeade97a370904b 100644
--- a/app/ui/legacy/src/main/res/values-eu/strings.xml
+++ b/app/ui/legacy/src/main/res/values-eu/strings.xml
@@ -27,9 +27,6 @@
Ikusi zer den berria bertsio honetan
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -298,9 +295,6 @@
Autentifikatzen\u2026
Kontuaren ezarpenak eskuratzen\u2026
Bertan behera uzten\u2026
- Ia amaitu duzu!
- Eman izen bat kontu honi (aukerakoa):
- Idatzi zure izena (irteerako mezuetan bistaratuko da):
Kontu mota
Zein kontu mota da hau?
POP3
@@ -351,7 +345,6 @@
Autentifikazioa
\"%1$s = %2$s \" ez da baliozkoa \"%3$s = %4$s \"-(r)ekin
Konfigurazio baliogabea: %s
- Kontu aukerak
Karpeta atzitzeko maiztasuna
Inoiz ez
15 minutuero
@@ -371,7 +364,6 @@
36 minutuero
48 minutuero
60 minutuero
- Jakinarazi niri posta iristean
Bistaratuko den mezu kopurua
10 mezu
25 mezu
@@ -1005,9 +997,24 @@
Autocrypt ezarpen mezua
Mezu honek zure Autocrypt ezarpenak eta gako sekretuak jatorrizko gailutik partekatzeko informazio guztia du.
-Zure gailu berrian Autocrypt ezartzeko, jarraitu gailu berrian agertuko zaizkizun argibideak.
+zure gailu berrian autocrypt ezartzeko, jarraitu gailu berrian agertuko zaizkizun argibideak.
-Mezu hau gorde dezakezu eta zure gako sekretuaren babes-kopia gisa erabili. Hau egin nahi baduzu, pasahitza idatzi beharko zenuke eta toki seguruan gorde.
+mezu hau gorde dezakezu eta zure gako sekretuaren babes-kopia gisa erabili. hau egin nahi baduzu, pasahitza idatzi beharko zenuke eta toki seguruan gorde.
+
+ Ikusi
+
+ honi: %s
+
+ +
+
+ niri
+
+ Erakutsi urruneko irudiak
+
+ Dentsitatea
+ Trinkoa
+ Erlaxatua
+ Hartzaileak
Errore bat gertatu da mezua bidaltzean. Egiaztatu zure sare konexioa eta mezuak bidaltzeko zerbitzariaren konfigurazioa.
Bai
Ez
@@ -1076,4 +1083,24 @@ Mezu hau gorde dezakezu eta zure gako sekretuaren babes-kopia gisa erabili. Hau
Posta helbidea
+
+ Mezuaren xehetasunak
+
+ \'Data\' goiburua falta da
+
+ Errorea gertatu da mezuaren xehetasunak kargatzean.
+
+ Gehitu kontaktuetara
+
+ Idatzi mezua honi:
+
+ Kopiatu e-posta helbidea
+
+ Kopiatu izena eta e-posta helbidea
+
+ Izena eta e-posta helbidea
+
+ Kontua ezabatzen…
+
+ irakurri gabea, %s
diff --git a/app/ui/legacy/src/main/res/values-fa/strings.xml b/app/ui/legacy/src/main/res/values-fa/strings.xml
index 1ce8831ff2ffa1672944ea02e488442cf52aa617..1f1a98ed257e80641a9c878f3a5b5abda077f102 100644
--- a/app/ui/legacy/src/main/res/values-fa/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fa/strings.xml
@@ -26,9 +26,6 @@
از تازههای این نسخه باخبر شوید
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -291,9 +288,6 @@
احراز هویت\u2026
واکشی تنظیمات حساب کاربری\u2026
لغوکردن\u2026
- تقریباً تمام شد!
- نامی برای این حساب انتخاب کنید (اختیاری):
- نام خود را بنویسید (در پیامهای ارسالی نمایش داده میشود):
نوع حساب کاربری
نوع این حساب کاربری چیست؟
POP3
@@ -344,7 +338,6 @@
احراز هویت
«%1$s = %2$s » با «%3$s = %4$s » معتبر نیست
پیکربندی نادرست: %s
- گزینههای حساب کاربری
تکرار سرکشی به پوشه
هرگز
هر ۱۵ دقیقه
@@ -364,7 +357,6 @@
هر ۳۶ دقیقه
هر ۴۸ دقیقه
هر ۶۰ دقیقه
- هر زمان رایانامه رسید به من خبر بده
تعداد پیامها برای نمایش
۱۰ پیام
۲۵ پیام
@@ -999,13 +991,16 @@
میتوانید این پیام را پیش خود نگه دارید و آن را بهعنوان پشتیبان کلید سرّیتان بهکار ببرید. برای انجام این کار باید گذرواژه را یادداشت و در جای امنی نگهداری کنید.
+
+ به %s
+
+ +
+
کد منبع
نما
انتقال به…
کپی به…
-
- به
من
@@ -1122,4 +1117,7 @@
نام و آدرس ایمیل
+ در حال حذف حساب…
+
+ خوانده نشده، %s
diff --git a/app/ui/legacy/src/main/res/values-fi/strings.xml b/app/ui/legacy/src/main/res/values-fi/strings.xml
index a2f7b694f6392cde81bf348ff8e525c64b7c9ee0..90c633bbc436935ccbfd42c73711c4d2668ad0af 100644
--- a/app/ui/legacy/src/main/res/values-fi/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fi/strings.xml
@@ -25,37 +25,6 @@
Mitä uutta
Näytä viimeisimmät muutokset kun sovellus päivitettiin
Selvitä mitä uutta tämä julkaisu sisältää
-
- Tervetuloa Mail Mailiin
-
-Mail on monipuolinen ilmainen sähköpostisovellus Androidille.
-
-Sen ominaisuuksiin kuuluu muun muassa:
-
-
- - Push-viestit käyttäen IMAP IDLE -toteutusta
- - Aiempaa parempi suorituskyky
- - Viestien arkistointi
- - Sähköpostin allekirjoitukset
- - Piilokopioiden lähetys itselle
- - Kansiotilaukset
- - Kaikkien kansioiden synkronointi
- - Vastausosoitteen määritys
- - Pikanäppäimet
- - Aiempaa parempi IMAP-tuki
- - Liitteiden tallennus muistikortille
- - Roskakorin tyhjennys
- - Viestien järjestely
- - …ja paljon muuta
-
-
-Huomioithan, että Mail ei tue useimpia ilmaisia Hotmail-tilejä, ja kuten monet muut sovellukset, saattaa kärsiä pienistä ongelmista Microsoft Exchange -ympäristössä.
-
-Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoitteessa
-https://github.com/k9mail/k-9/.
-
- ]]>
-- \nLähetetty Android-laitteestani Mail Maililla. Pahoittelut vähäsanaisuudestani.
@@ -316,9 +285,6 @@ Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoittees
Varmistetaan\u2026
Haetaan tilin asetuksia\u2026
Peruutetaan\u2026
- Melkein valmista!
- Anna tilin nimi (ei pakollinen):
- Anna nimesi (näkyy lähtevissä viesteissä):
Tilin tyyppi
Mikä tämän tilin tyyppi on?
POP3
@@ -369,7 +335,6 @@ Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoittees
Todennustapa
\"%1$s = %2$s \" ei ole kelvollinen asetuksen \"%3$s = %4$s \" kanssa
Virheellinen asetus: %s
- Tilin asetukset
Viestien tarkistus
Ei koskaan
Joka 15 minuutti
@@ -389,7 +354,6 @@ Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoittees
Joka 36 minuutti
Joka 48 minuutti
Joka 60 minuutti
- Ilmoitus postin saapumisesta
Näytettävien viestien lukumäärä
10 kansiota
25 kansiota
@@ -1024,6 +988,23 @@ Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoittees
Aseta uusi laitteesi käyttämään Autocryptiä noudattamalla ohjeita, jotka uusi laitteesi näyttää.
Voit säilyttää tämän viestin ja käyttää sitä varmuuskopioina salausavaimellesi. Jos haluat tehdä tämän, kirjoita salasana muistiin ja säilytä se turvallisesti.
+
+ vastaanottaja %s
+
+ +
+
+ minä
+
+ Näytä etäkuvat
+
+ Tiheys
+ Kompakti
+ Oletus
+ Rento
+ Lähetä asiakasohjelmiston ID-tunniste
+
+ Tilin nimi
+ Vastaanottajat
Viestiä lähettäessä tapahtui virhe. Tarkista verkkoyhteyden tila ja lähtevän postin palvelimen asetukset.
Päällä
Pois
@@ -1103,4 +1084,8 @@ Voit säilyttää tämän viestin ja käyttää sitä varmuuskopioina salausavai
Nimi ja sähköpostiosoitteet
+
+ Poistetaan tili…
+
+ lukematon, %s
diff --git a/app/ui/legacy/src/main/res/values-fr/strings.xml b/app/ui/legacy/src/main/res/values-fr/strings.xml
index 87e4a2a80a56731b8915f932db546cbadb21ca05..64ecc26ffb1959a0dc8819de1a93451e93638083 100644
--- a/app/ui/legacy/src/main/res/values-fr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fr/strings.xml
@@ -25,9 +25,6 @@
Nouveautés
Afficher les changements récents quand l’appli a été mise à jour
Découvrez les nouveautés de cette version
-
- Bienvenue sur Mail
- Mail est le client de messagerie par défaut de /e/OS
-- Envoyé depuis /e/OS Mail.
@@ -269,9 +266,6 @@ jusqu’à %d de plus
Authentification\u2026
Récupération des paramètres du compte\u2026
Annulation\u2026
- Vous avez presque terminé !
- Donner un nom à ce compte (facultatif) :
- Saisissez votre nom (s’affiche dans les courriels sortants) :
Type de compte
Quel type de compte est-ce ?
POP3
@@ -322,7 +316,6 @@ jusqu’à %d de plus
Authentification
« %1$s = %2$s » n’est pas valide avec « %3$s = %4$s »
Paramétrage invalide : %s
- Options du compte
Fréquence de scrutation des dossiers
Jamais
Toutes les 15 minutes
@@ -342,7 +335,6 @@ jusqu’à %d de plus
Toutes les 36 minutes
Toutes les 48 minutes
Toutes les 60 minutes
- M’avertir lors de l’arrivée de courriels
Nombre de courriels à afficher
10 courriels
25 courriels
@@ -1064,14 +1056,12 @@ jusqu’à %d de plus
À
Répondre à
Détails du message
- "à "
moi
Copier l\'adresse e-mail
Expéditeur
Copier le nom et l\'adresse e-mail
Adresse e-mail
Nom et adresse e-mail
- Paramétrez un nouveau compte ou importez des comptes e-mail depuis un fichier de paramétrages.
+
@@ -1085,4 +1075,11 @@ jusqu’à %d de plus
Rédiger un courriel
-
\ No newline at end of file
+
+ Afficher
+
+
+ Suppression du compte…
+
+ non lus, %s
+
diff --git a/app/ui/legacy/src/main/res/values-fy/strings.xml b/app/ui/legacy/src/main/res/values-fy/strings.xml
index 46f86b8c08c78d1c20ad579c687fbeaf4536ac10..4e9953213a9e7d09db418e3a618f912e2809b60d 100644
--- a/app/ui/legacy/src/main/res/values-fy/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fy/strings.xml
@@ -28,33 +28,6 @@
Untdek wat der nij is yn dizze ferzje
Besjen
-
- Wolkom by Mail
- Mail is in fergees krêftige e-mailclient foar Android. De ferbettere mooglikheden bestean ûnder oare út:
-
-
-- Pushmail middels IMAP IDLE
-- Bettere prestaasjes
-- Berjocht werklassifikaasje
-- E-mailhantekeningen
-- Bcc nei josels
-- Mapabonneminten
-- Syngronisaasje fan alle mappen
-- Antwurdadres ynstelle
-- Toetseboerd fluchkeppelingen
-- Bettere stipe IMAP
-- Bylage bewarje nei SD
-- Jiskefet leegje
-- Berjochten sortearje
-- en mear…
-
-
-Hâld der rekkening mei dat Mail de measte fergeze Hotmail-accounts net stipet, en krekt as in protte e-mailclients, problemen hawwe kin om te ferbinen mei Microsoft Exchange.
-
-Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
-https://github.com/k9mail/k-9/.
-
-]]>
-- \nFerstjoerd fan myn Android-apparaat ôf mei Mail.
@@ -218,8 +191,6 @@ Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
Cc:
Bcc:
Kin bylage net bewarje.
-
- "oan "
+
@@ -326,9 +297,6 @@ Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
Autentikaasje\u2026
Accountynstellingen ophelje\u2026
Annulearje\u2026
- Hast klear!
- Jou dit account in namme (opsjoneel):
- Typ jo namme (sichtber by útgeande berjochten):
Accounttype
Hokker type account is dit?
POP3
@@ -380,7 +348,6 @@ Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
Autentikaasjetype
‘%1$s = %2$s ’ is net jildich mei ‘%3$s = %4$s ’
Unjildige ynstellingen: %s
- Accountopsjes
Frekwinsje mapkontrôle
Nea
Elke 15 minuten
@@ -400,7 +367,6 @@ Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
Elke 36 minuten
Elke 48 minuten
Elke 60 minuten
- Melding jaan by nij e-mailberjocht
Tal berjochten om te toanen
10 berjochten
25 berjochten
@@ -1114,4 +1080,7 @@ Jo kinne dit berjocht bewarje as reservekopy foar jo geheime kaai. As jo dit dwa
Namme en e-mailadres
+ Account fuortsmite…
+
+ net lêzen, %s
diff --git a/app/ui/legacy/src/main/res/values-gd/strings.xml b/app/ui/legacy/src/main/res/values-gd/strings.xml
index fc064c6a484687675f54f48c4b4d7d1a3921255b..e5ca6011838bbdd32cfecc99ca07b1fb1cf09743 100644
--- a/app/ui/legacy/src/main/res/values-gd/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gd/strings.xml
@@ -23,9 +23,6 @@
Na tha ùr
-
- Fàilte gu aplacaid a’ phuist
- <p> Tha aplacaid a’ phuist ’na aplacaid shaor agus chumhachdach airson post-d air Android. </p><p> Tha na gleusan leasaichte aige a’ gabhail a-staigh: </p> <ul> <li>Put post slighe IMAP IDLE</li> <li>Dèanadas nas fheàrr</li> <li>Ath-fhaidhleadh theachdaireachdan</li> <li>Eàrr-sgrìobhaidhean puist-d</li> <li>Bcc dhut fhèin</li> <li>Fo-sgrìobhaidhean phasganan</li> <li>Sioncronachadh nam pasgan air fad</li> <li>Rèiteachadh seòladh nam freagairt</li> <li>Ath-ghoiridean a’ mheur-chlàir</li> <li>Taic nas fheàrr airson IMAP</li> <li>Sàbhaladh de cheanglachain air SD</li> <li>Falamhachadh an sgudail</li> <li>Seòrsachadh theachdaireachdan</li> <li>…is mòran a bharrachd</li> </ul> <p> Thoir an aire nach cuir aplacaid a’ phuist taic ri cuid mhòr de chunntasan saora Hotmail agus mar iomadh prògram puist-d eile, tachraidh rudan neònach nuair a bhruidhneas e ri Microsoft Exchange. </p><p> Fàilte air bugaichean, com-pàirteachas, iarrtasan airson gleusan ùra is ceistean aig <a href=https://github.com/k9mail/k-9/>https://github.com/k9mail/k-9/</a>. </p>
-- Chaidh a chur le post /e/.
@@ -266,9 +263,6 @@
’Ga dhearbhadh…
A’ faighinn roghainnean a’ chunntais…
A’ sgur dheth…
- Cha mhòr deiseil!
- Thoir ainm air a’ chunntas seo
- Cuir a-steach d’ ainm (chithear seo ann an teachdaireachdan a thèid a-mach):
Seòrsa a’ chunntais
Dè seòrsa cunntas a tha seo\?
POP3
@@ -317,7 +311,6 @@
Dearbhadh
Chan eil “%1$s = %2$s ” dligheach mu choinneamh “%3$s = %4$s ”
Suidheachadh mì-dhligheach: %s
- Roghainnean a’ chunntais
Dè cho tric ’s a dh’iarras sinn am post\?
Chan ann idir
Gach cairteal na h-uarach
@@ -337,7 +330,6 @@
Gach 36 mionaid
Gach 48 mionaid
Gach uair a thìde
- Thoir brath dhomh nuair a ruigeas post
Co mheud teachdaireachd a thèid a shealltainn
10 teachdaireachdan
25 teachdaireachd
@@ -928,4 +920,5 @@
Seòladh puist-d
+
diff --git a/app/ui/legacy/src/main/res/values-gl-rES/strings.xml b/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
index c31c7c383d44ecb68c541524b2cb8c8bc75a321c..486fffa0c59e12fd3e105c801fb6fc7cae24185c 100644
--- a/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
@@ -7,8 +7,6 @@
Mail Accounts
Unread emails
-
- Welcome to Mail
-- Sent from /e/ Mail.
@@ -206,9 +204,6 @@
Autenticando\u2026
Obtendo os axustes da conta\u2026
Cancelando\u2026
- Feito!
- Darlle un nome a esta conta (opcional):
- Escribe o teu nome (mostrarase nas mensaxes que envíes):
Tipo de conta
Que tipo de conta é esta?
POP3
@@ -223,7 +218,6 @@
Certificado do cliente
Servidor POP3
Servidor IMAP
- Servidor Exchange
Porto
Seguranza
Autenticación
@@ -247,9 +241,6 @@
Correo lixo
Ver só cartafoles subscritos
Expandir automaticamente cartafol
- Camiño OWA
- Camiño de autenticación
- Alcume da caixa de correo
Axustes do servidor de saída
Servidor SMTP
Porto
@@ -259,7 +250,6 @@
Contrasinal
Autenticación
Configuración non válida: %s
- Opcións da conta
Frecuencia de comprobación de correo
Nunca
Cada 15 minutos
@@ -279,7 +269,6 @@
Cada 36 minutos
Cada 48 minutos
Cada 60 minutos
- Avisarme cando chegue unha mensaxe
Número de mensaxes para mostrar
10 mensaxes
25 mensaxes
diff --git a/app/ui/legacy/src/main/res/values-gl/strings.xml b/app/ui/legacy/src/main/res/values-gl/strings.xml
index 29cd2ee8c6a6da826e4053328268960dc2b31b54..f231f18e1388e15635e3ee3825440b2addaa0174 100644
--- a/app/ui/legacy/src/main/res/values-gl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gl/strings.xml
@@ -25,9 +25,6 @@
Mira as novidades deste lanzamento
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -278,9 +275,6 @@
Autentificando\u2026
Obtendo Configuración da conta\u2026
Cancelando\u2026
- Feito!
- Dalle un nome a esta conta (opcional):
- Introduce o teu nome (visualizarase na mensaxe de saída):
Tipo de conta
Qué tipo de conta é?
POP3
@@ -329,7 +323,6 @@
Tipo de autentificación
\"%1$s = %2$s \" non é válido con \"%3$s = %4$s \"
Configuración inválida: %s
- Opcións de conta
Frecuencia de comprobación de correo
Nunca
Cada 15 minutos
@@ -349,7 +342,6 @@
Cada 36 minutos
Cada 48 minutos
Cada 60 minutos
- Avisarme cando chegue correo
Número de mensaxes a visualizar
10 mensaxes
25 mensaxes
@@ -960,6 +952,16 @@ Para configurar o teu novo dispositivo para Autocrypt, sigue as instrucións que
Podes conservar esta mensaxe e usala como copia de seguridade da túa chave secreta. Se queres facelo, debes anotar o contrasinal e gardalo de forma segura.
+ Mover a…
+ Copiar a…
+
+ - Para poder usar a conta \"
%s \" cómpre proporcionar o contrasinal do servidor.
+ - Para poder usar a conta \"
%s \" cómpre proporcionar os contrasinais do servidor.
+
+ Contrasinal do servidor entrante
+ Contrasinal do servidor saínte
+ Use o mesmo contrasinal para o servidor de saída
+ Nome do servidor: %s
Produciuse un erro ao enviar a mensaxe. Comprobe a conectividade da súa rede e a configuración do servidor de saída.
Acender
Apagar
@@ -1023,4 +1025,5 @@ Podes conservar esta mensaxe e usala como copia de seguridade da túa chave secr
Enderezo electrónico
+
diff --git a/app/ui/legacy/src/main/res/values-hr/strings.xml b/app/ui/legacy/src/main/res/values-hr/strings.xml
index 5337066c4c82e557ad9f2917e4901342f8ebe19e..5395c861c4163c339cf9bf8864e7318612394da8 100644
--- a/app/ui/legacy/src/main/res/values-hr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-hr/strings.xml
@@ -13,8 +13,6 @@
Licenca
-
- Welcome to Mail
-- Sent from /e/ Mail.
@@ -259,9 +257,6 @@
Identifikacija\u2026
Dohvaćam postavke računa\u2026
Otkazujem\u2026
- Uskoro ste gotovi!
- Dajte naziv ovom računu (opcionalno):
- Unesite vaše ime (prikazuje se na odlaznim porukama):
Vrsta računa
Kakav je to račun?
POP3
@@ -310,7 +305,6 @@
Identifikacija
\"%1$s = %2$s \" nije važeća sa \"%3$s = %4$s \"
Nevažeće postavljanje: %s
- Opcije računa
Učestalost provjera mapa
Nikad
Svakih 15 minuta
@@ -330,7 +324,6 @@
Svakih 36 minuta
Svakih 48 minuta
Svakih 60 minuta
- Obavijesti me kod primitka pošte
Broj poruka za prikaz
10 poruka
25 poruka
@@ -960,4 +953,5 @@
Adresa e-pošte
+
diff --git a/app/ui/legacy/src/main/res/values-hu/strings.xml b/app/ui/legacy/src/main/res/values-hu/strings.xml
index e423a3c36c4107ae447fd9b2d371d9c0243138c4..78927845f28b34048d83ea0560f674bfd66185be 100644
--- a/app/ui/legacy/src/main/res/values-hu/strings.xml
+++ b/app/ui/legacy/src/main/res/values-hu/strings.xml
@@ -27,9 +27,6 @@
Tudja meg, hogy mik az újdonságok ebben a kiadásban
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -297,9 +294,6 @@
Hitelesítés\u2026
Fiókbeállítások beolvasása\u2026
Megszakítás\u2026
- Már majdnem kész.
- A fiók elnevezése (opcionális):
- A név megadása (kimenő üzeneteknél jelenik meg):
Fióktípus
Milyen fajta fiók ez?
POP3
@@ -350,7 +344,6 @@
Hitelesítés
„%1$s = %2$s ” nem érvényes ezzel: „%3$s = %4$s ”
Érvénytelen beállítás: %s
- Fiók beállításai
Mappa lekérdezésének gyakorisága
Soha
15 percenként
@@ -370,7 +363,6 @@
36 percenként
48 percenként
60 percenként
- Értesítsen, amikor levél érkezik
Megjelenítendő üzenetek száma
10 üzenet
25 üzenet
@@ -1008,8 +1000,6 @@ Megtarthatja ezt az üzenetet, és felhasználhatja a titkos kulcs biztonsági m
Megtekintés
Áthelyezés…
Másolás…
-
- ->
+
@@ -1113,4 +1103,7 @@ Megtarthatja ezt az üzenetet, és felhasználhatja a titkos kulcs biztonsági m
Név és e-mail cím
+ Fiók eltávolítása…
+
+ olvasatlan, %s
diff --git a/app/ui/legacy/src/main/res/values-in/strings.xml b/app/ui/legacy/src/main/res/values-in/strings.xml
index d90fbc93072c785eb2ad2e87044d6da3846ba30c..36c8ab435202a14bc26ff6dc7efd8285f67cea6a 100644
--- a/app/ui/legacy/src/main/res/values-in/strings.xml
+++ b/app/ui/legacy/src/main/res/values-in/strings.xml
@@ -25,9 +25,6 @@
Apa yang baru
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -269,9 +266,6 @@
Otentikasi\u2026
Mengambil pengaturan akun\u2026
Membatalkan\u2026
- Anda hampir selesai!
- Berikan akun ini sebuah nama (opsional):
- Ketik nama Anda (ditampilkan pada pesan keluar):
Tipe akun
Akun apa ini?
POP3
@@ -320,7 +314,6 @@
Otentikasi
\"%1$s = %2$s \" tidak valid dengan \"%3$s = %4$s \"
Salah pengaturan: %s
- Opsi akun
Frekuensi penarikan folder
Tidak pernah
Setiap 15 menit
@@ -340,7 +333,6 @@
Setiap 36 Menit
Setiap 48 Menit
Setiap 60 Menit
- Beritahu saya bila surat masuk
Jumlah pesan yang akan ditampilkan
10 pesan
25 pesan
@@ -994,4 +986,5 @@ Anda dapat menyimpan pesan ini dan menggunakannya sebagai cadangan untuk kunci r
Alamat surel
+
diff --git a/app/ui/legacy/src/main/res/values-is/strings.xml b/app/ui/legacy/src/main/res/values-is/strings.xml
index d437038f6fd7e087833f29e057a74d20ce997f31..8bd1727e15f7555e892b5c1aae68d4b87f277b16 100644
--- a/app/ui/legacy/src/main/res/values-is/strings.xml
+++ b/app/ui/legacy/src/main/res/values-is/strings.xml
@@ -25,9 +25,6 @@
Nýtt á döfinni
Birta nýlegar breytingar þegar forritið er uppfært
Skoðaðu hvað sé nýtt við þessa útgáfu
-
- Velkomin í Póstinn
- Póstur/Mail er sjálfgefið póstforrit fyrir /e/OS
-- Sent úr /e/OS Póstinum.
@@ -294,9 +291,6 @@
Auðkenni…
Næ í stillingar aðgangs…
Hætti við…
- Þetta er næstum búið!
- Gefðu þessum aðgangi nafn (valkvætt):
- Skrifaðu nafnið þitt (er birt á útsendum skilaboðum):
Gerð aðgangs
Hvers konar aðgangur er þetta\?
POP3
@@ -347,7 +341,6 @@
Auðkenning
\"%1$s = %2$s \" er ekki gilt með \"%3$s = %4$s \"
Ógild uppsetning: %s
- Valkostir aðgangs
Tíðni athugana á möppu
Aldrei
Á 15 mínútna fresti
@@ -367,7 +360,6 @@
Á 36 mínútna fresti
Á 48 mínútna fresti
Á 60 mínútna fresti
- Láta mig vita þegar nýr póstur berst
Fjöldi skilaboða sem á að birta
10 skilaboð
25 skilaboð
@@ -1045,8 +1037,6 @@
Ruslpóstur
Færa…
- Settu upp nýjan aðgang eða flyttu inn tölvupóstaðganga úr áður notaðri stillingaskrá.
- "til "
+
mín
Birta fjartengdar myndir
@@ -1103,6 +1093,7 @@
Nafn aðgangsins
Semja póst til
- ólesið, %s
Fjarlægi aðgang…
-
\ No newline at end of file
+
+ ólesið, %s
+
diff --git a/app/ui/legacy/src/main/res/values-it/strings.xml b/app/ui/legacy/src/main/res/values-it/strings.xml
index e4a890cb84c96310156eed37678f036b528f3482..acc724d0f634317932702b941c4034cb7deeb44c 100644
--- a/app/ui/legacy/src/main/res/values-it/strings.xml
+++ b/app/ui/legacy/src/main/res/values-it/strings.xml
@@ -23,9 +23,6 @@
Cosa c\'è di nuovo
Mostra le ultime modifiche quando l\'app viene aggiornata
Scopri le novità di questa versione
-
- Benvenuto in Mail
- Mail è il client mail di default di /e/OS
-- Inviato da /e/OS Mail.
@@ -259,9 +256,6 @@
Autenticazione…
Recupero delle impostazioni dell\'account in corso…
Annullamento in corso…
- Hai quasi fatto!
- Assegna un nome all\'account (facoltativo):
- Scrivi il tuo nome (visualizzato nei messaggi in uscita):
Tipo di account
Che tipo di account è questo\?
POP3
@@ -312,7 +306,6 @@
Autenticazione
\"%1$s = %2$s \" non è valido con \"%3$s = %4$s \"
Configurazione non valida: %s
- Opzioni account
Frequenza di verifica cartella
Mai
Ogni 15 minuti
@@ -332,7 +325,6 @@
Ogni 36 minuti
Ogni 48 minuti
Ogni 60 minuti
- Invia notifica all\'arrivo di nuovi messaggi
Numero di messaggi da visualizzare
10 messaggi
25 messaggi
@@ -1074,10 +1066,8 @@ Puoi conservare questo messaggio e utilizza come una copia di sicurezza della tu
Mostra immagini su server remoto
me
+
- "A "
Permetti all\'app di accedere alle Email.
Elimina Email
- Imposta un nuovo account o importa i tuoi account da un file di impostazioni esistente.
Leggi Email
Visualizza
Compatti
@@ -1088,4 +1078,7 @@ Puoi conservare questo messaggio e utilizza come una copia di sicurezza della tu
Nome Account
Scrivi messaggio per
+ Rimozione account…
+
+ nuovi messaggi, %s
diff --git a/app/ui/legacy/src/main/res/values-iw/strings.xml b/app/ui/legacy/src/main/res/values-iw/strings.xml
index 08424865b5695f6196303fe4a6b73c9221663ad8..4d85aef104d8ac3fda709f4fe0f8ecae3a4e4235 100644
--- a/app/ui/legacy/src/main/res/values-iw/strings.xml
+++ b/app/ui/legacy/src/main/res/values-iw/strings.xml
@@ -20,8 +20,6 @@
גלה מה חדש בגירסא זו
-
- Welcome to Mail
-- Sent from /e/ Mail.
@@ -131,7 +129,7 @@
בעיה בתעודה עבור %s
בדוק את הגדרות השרת
נכשל באימות
- האימות נכשל עבור %s .
+ האימות נכשל עבור %s .
עדכן את הגדרות השרת.
@@ -254,9 +252,6 @@
מאמת …
מייבא הגדרות החשבון …
מבטל…
- כמעט מוכן!
- תן שם לחשבון זה (לא חובה):
- הקלד את שמך (מוצג בהודעות יוצאות):
סוג חשבון
איזה סוג חשבון זה?
POP3
@@ -296,7 +291,6 @@
סיסמא
סו אימות
הגדרה לא חוקית: %s
- אפשרויות חשבון
אפ פעם
כל רבע שעה
כל 30 דקות
@@ -315,7 +309,6 @@
כל 36 דקות
כל 48 דקות
כל 60 דקות
- הודע לי כאשר מגיע דואר
מספר הודעות להצגה
10 הודעות
25 הודעות
@@ -648,4 +641,5 @@
כתובת דוא\"ל
+
diff --git a/app/ui/legacy/src/main/res/values-ja/strings.xml b/app/ui/legacy/src/main/res/values-ja/strings.xml
index 91d80d5ffcd616bc707e1a4b793a796ce63d59db..f838a35df88ddffac85c5c44b37d331c213e0d09 100644
--- a/app/ui/legacy/src/main/res/values-ja/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ja/strings.xml
@@ -28,9 +28,6 @@
このリリースは何が新しいのかを知る
表示
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -303,9 +300,6 @@
認証中\u2026
アカウント設定を取得中\u2026
中断\u2026
- 電子メールセットアップ
- アカウントの名前を付けます (オプション):
- 名前を入力します (送信メールの表示名):
メールアカウント種類の選択
受信メールサーバの種類を選択して下さい
POP3 サーバ
@@ -357,7 +351,6 @@
セキュリティ設定
\"%1$s = %2$s \" は \"%3$s = %4$s \" で無効です
無効な設定: %s
- アカウントオプション
同期フォルダの同期間隔
しない
15分毎
@@ -377,7 +370,6 @@
36分毎
48分毎
60分毎
- メール到着時に通知
受信メールを表示する件数
10件
25件
@@ -1088,4 +1080,8 @@
名前とメールアドレス
+
+ アカウントを削除しています…
+
+ 未読、%s
diff --git a/app/ui/legacy/src/main/res/values-ko/strings.xml b/app/ui/legacy/src/main/res/values-ko/strings.xml
index d38bf12e777f74e46008a723a108d2a59518083f..15d75dd7218fd10f8e98828490e2c87d9c229013 100644
--- a/app/ui/legacy/src/main/res/values-ko/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ko/strings.xml
@@ -26,8 +26,6 @@
이 릴리즈의 새로운 기능 알아보기
-
- Welcome to Mail
-- Sent from /e/ Mail.
@@ -268,9 +266,6 @@
인증 중\u2026
계정 설정 가져 오기\u2026
취소 중\u2026
- 설정이 거의 완료되었습니다!
- 이 계정의 이름을 입력 (옵션):
- 이름 입력 (보내는 메시지에 표시 될 이름):
계정 종류
계정 유형을 선택하시오.
POP3
@@ -317,7 +312,6 @@
비밀번호
인증 유형
잘못된 설정: %s
- 계정 옵션
폴더 수신 빈도
하지 않음
15분마다
@@ -337,7 +331,6 @@
36분마다
48분마다
60분마다
- 메일 도착시 알림
화면에 보여질 메시지 수
10 메시지
25 메시지
@@ -848,4 +841,5 @@
전자우편 주소
+
diff --git a/app/ui/legacy/src/main/res/values-lt/strings.xml b/app/ui/legacy/src/main/res/values-lt/strings.xml
index f2c736f9c8d0c9e5e113bc216bfafc4c2c9865ef..01a5e2eaddef71b7e1f18eb7a4ad4804c5490577 100644
--- a/app/ui/legacy/src/main/res/values-lt/strings.xml
+++ b/app/ui/legacy/src/main/res/values-lt/strings.xml
@@ -27,8 +27,6 @@
Sužinokite, kas naujo šioje versijoje
-
- Welcome to Mail
- \nIšsiųsta iš mano Android įrenginio naudojant Mail. Atleiskite už trumpumą.
@@ -198,7 +196,6 @@
Bcc:
Nepavyko išsaugoti priedo.
- "kam "
+
@@ -297,9 +294,6 @@
Patvirtinama tapatybė\u2026
Gaunami paskyros nustatymai\u2026
Atšaukiama\u2026
- Beveik baigėte!
- Pavadinkite šią paskyrą (nebūtina):
- Įrašykite savo vardą (rodoma siunčiamuose pranešimuose):
Paskyros tipas
Kokios rūšies ši paskyra?
POP3
@@ -350,7 +344,6 @@
Tapatumo nustatymas
\"%1$s = %2$s \" netinka su \"%3$s = %4$s \"
Netinkamai nustatyta: %s
- Paskyros parinktys
Aplanko tikrinimo dažnis
Niekada
Kas 15 minučių
@@ -370,7 +363,6 @@
Kas 36 minutes
Kas 48 minutes
Kas 60 minučių
- Pranešti apie naujus laiškus
Rodyti laiškų
10 laiškų
25 laiškai
@@ -1018,6 +1010,8 @@ Norėdami nustatyti automatinį šifravimą naujajame prietaise, vadovaukitės i
Šią žinutę galite pasilikti ir naudoti kaip atsarginę slaptojo rakto kopiją. Jei norite tai padaryti, turėtumėte užsirašyti slaptažodį ir saugiai jį laikyti.
+
+ kam %s
Siunčiant laišką įvyko klaida. Patikrinkite tinklo ryšį ir siunčiamo serverio konfigūraciją.
Įj.
Išj.
@@ -1090,4 +1084,5 @@ Norėdami nustatyti automatinį šifravimą naujajame prietaise, vadovaukitės i
Vardas ir el. pašto adresas
+
diff --git a/app/ui/legacy/src/main/res/values-lv/strings.xml b/app/ui/legacy/src/main/res/values-lv/strings.xml
index 41625da506f7c15e5d6e5fe0bc2f2eb815cb21e5..894341d3783c1ddd78f36a8c104d3448e9d3ac21 100644
--- a/app/ui/legacy/src/main/res/values-lv/strings.xml
+++ b/app/ui/legacy/src/main/res/values-lv/strings.xml
@@ -28,9 +28,6 @@
Uzzināt, kas jauns šajā versijā
Skatīt
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -201,8 +198,6 @@ pat %d vairāk
Cc:
Neredzamais:
Nevar saglabāt pielikumu.
-
- "kam "
+
@@ -309,9 +304,6 @@ pat %d vairāk
Pārbauda identifikāciju\u2026
Saņem konta iestatījumus\u2026
Atceļ\u2026
- Gandrīz viss pabeigts!
- Nosaukt kontu (nav obligāti):
- Ierakstiet savu vārdu (attēlošanai izejošajās vēstulēs):
Konta veids
Kāda veida konts tas ir?
POP3
@@ -363,7 +355,6 @@ pat %d vairāk
Identitātes pārbaude
\"%1$s = %2$s \" nav derīgs ar \"%3$s = %4$s \"
Nederīgi iestatījumi: %s
- Konta iespējas
Mapes pārbaudes biežums
Nekad
Katras 15 minūtes
@@ -383,7 +374,6 @@ pat %d vairāk
Katras 36 minūtes
Katras 48 minūtes
Katras 60 minūtes
- Paziņot, kad saņemts jauns pasts
Vēstuļu skaits, ko parādīt
10 vēstules
25 vēstules
@@ -1027,6 +1017,8 @@ Lai jaunajā ierīcē iestatītu automātisko šifrēšanu, lūdzu sekojiet nor
Šo vēstuli var saglabāt un izmantot kā rezerves kopiju šifra atslēgai. Ja vēlaties to darīt, pierakstiet paroli un nolieciet drošā vietā.
+
+ kam %s
Kļūdas dēl neizdevās nosūtīt vēstuli. Lūdzu, pārbaudiet tīkla savienojumu un izejošā servera iestatījumus!
Ieslēgt
Izslēgt
@@ -1103,4 +1095,5 @@ Lai jaunajā ierīcē iestatītu automātisko šifrēšanu, lūdzu sekojiet nor
Vārds un e-pasta adrese
+
diff --git a/app/ui/legacy/src/main/res/values-ml/strings.xml b/app/ui/legacy/src/main/res/values-ml/strings.xml
index 0a7284c4dd8996c59d1d665c77c12e87b0da4e8c..162e2a6b1194feaeb783a0ef81d8706f31a7e062 100644
--- a/app/ui/legacy/src/main/res/values-ml/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ml/strings.xml
@@ -25,9 +25,6 @@
ഈ റിലീസിൽ പുതിയത് എന്താണെന്ന് കണ്ടെത്തുക
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -285,9 +282,6 @@
പ്രാമാണീകരിക്കുന്നു\u2026
അക്കൗണ്ട് ക്രമീകരണങ്ങൾ ലഭ്യമാക്കുന്നു\u2026
റദ്ദാക്കുന്നു\u2026
- നിങ്ങൾ ഏകദേശം പൂർത്തിയാക്കി!
- ഈ അക്കൗണ്ടിന് ഒരു പേര് നൽകുക (ഐച്ഛികം):
- നിങ്ങളുടെ പേര് ടൈപ്പുചെയ്യുക (ഔട്ട്ഗോയിംഗ് സന്ദേശങ്ങളിൽ പ്രദർശിപ്പിക്കുന്നു):
അക്കൗണ്ട് തരം
ഇത് ഏത് തരത്തിലുള്ള അക്കൗണ്ടാണ്?
POP3
@@ -336,7 +330,6 @@
പ്രമാണീകരണം
\"%1$s = %2$s \" \"%3$s = %4$s \" ഉപയോഗിച്ച് സാധുവല്ല
അസാധുവായ സജ്ജീകരണം: %s
- അക്കൗണ്ട് ഓപ്ഷനുകൾ
ഫോൾഡർ വോട്ടെടുപ്പ് ആവൃത്തി
ഒരിക്കലും
ഓരോ 15 മിനിറ്റിലും
@@ -356,7 +349,6 @@
ഓരോ 36 മിനിറ്റിലും
ഓരോ 48 മിനിറ്റിലും
ഓരോ 60 മിനിറ്റിലും
- മെയിൽ വരുമ്പോൾ എന്നെ അറിയിക്കുക
പ്രദർശിപ്പിക്കേണ്ട സന്ദേശങ്ങളുടെ എണ്ണം
10 സന്ദേശങ്ങൾ
25 സന്ദേശങ്ങൾ
@@ -1020,4 +1012,5 @@
ഇമെയിൽ വിലാസം
+
diff --git a/app/ui/legacy/src/main/res/values-nb/strings.xml b/app/ui/legacy/src/main/res/values-nb/strings.xml
index 38882888561839785752e0f7261c1b5424c43a78..d7762b194ef9c17e808fd45c4b47c1764673ee81 100644
--- a/app/ui/legacy/src/main/res/values-nb/strings.xml
+++ b/app/ui/legacy/src/main/res/values-nb/strings.xml
@@ -20,9 +20,6 @@
Hva er nytt?
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -264,9 +261,6 @@ til %d flere
Autentiserer\u2026
Henter kontoinnstillinger\u2026
Avbryter\u2026
- Du er nesten ferdig!
- Gi denne kontoen et navn (valgfritt):
- Skriv ditt navn (vises i utgående meldinger):
Kontotype
Hvilken type konto er dette?
POP3
@@ -315,7 +309,6 @@ til %d flere
Autentisering
\"%1$s = %2$s \" er ikke gyldig med \"%3$s = %4$s \"
Ugyldig oppsett: %s
- Kontoinnstillinger
Oppdateringsfrekvens for mappen
Aldri
Hvert kvarter
@@ -335,7 +328,6 @@ til %d flere
Hvert 36. minutt
Hvert 48. minutt
Hver time
- Varsle meg når e-post ankommer
Antall meldinger som skal vises
10 meldinger
25 meldinger
@@ -956,4 +948,5 @@ til %d flere
E-postadresse
+
diff --git a/app/ui/legacy/src/main/res/values-nl/strings.xml b/app/ui/legacy/src/main/res/values-nl/strings.xml
index d29693b4e24d3dde915522f34adc8d5ff10b462b..d12e4391b584335806673b926c5012419a68011c 100644
--- a/app/ui/legacy/src/main/res/values-nl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-nl/strings.xml
@@ -23,8 +23,6 @@
Wat is er nieuw
Laat veranderingen zien als de app recent is bijgewerkt
Ontdek wat er nieuw is in deze versie
-
- Welkom bij Mail
-- Verstuurd met /e/OS Mail.
@@ -250,9 +248,6 @@
Authenticatie\u2026
Accountinstellingen worden opgehaald\u2026
Annuleren\u2026
- Bijna klaar!
- Geef dit account een naam (optioneel):
- Typ je naam (zichtbaar bij uitgaande berichten):
Accounttype
Welk type account is dit?
POP3
@@ -303,7 +298,6 @@
Authenticatietype
\"%1$s = %2$s \" is niet geldig met \"%3$s = %4$s \"
Ongeldige setup: %s
- Accountopties
Map-peiling-frequentie
Nooit
Elke 15 minuten
@@ -323,7 +317,6 @@
Elke 36 minuten
Elke 48 minuten
Elke 60 minuten
- Waarschuw me wanneer nieuwe e-mail binnenkomt
Aantal berichten om te laten zien
10 berichten
25 berichten
@@ -988,7 +981,6 @@ U kunt dit bericht bewaren als backup voor uw geheime sleutel. Als u dit wilt do
\n
\nTik om de notificatie instellingen te openen.
Notificatie fout
- Mail is de standaard email app voor /e/OS
Hulp krijgen
Gebruikshandleiding
Geen
@@ -1066,8 +1058,6 @@ U kunt dit bericht bewaren als backup voor uw geheime sleutel. Als u dit wilt do
Toon externe afbeeldingen
mzelf
+
- "aan "
- Stel een nieuwe account in of importeer jouw email account vanuit een vorig configuratie bestand.
Standaard
Compact
Ontspannend
@@ -1077,4 +1067,7 @@ U kunt dit bericht bewaren als backup voor uw geheime sleutel. Als u dit wilt do
Bekijk
Verstuur cliënt-ID
+ Account verwijderen…
+
+ ongelezen, %s
diff --git a/app/ui/legacy/src/main/res/values-pl/strings.xml b/app/ui/legacy/src/main/res/values-pl/strings.xml
index 11cb8a5ca03b965abaed793fe5ce31c8f3989ca3..135466d44bfee3d9152bbc9b5cb82e20f64ff41e 100644
--- a/app/ui/legacy/src/main/res/values-pl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pl/strings.xml
@@ -28,9 +28,6 @@
Dowiedz się, co nowego w tej wersji
Wyświetl
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -198,8 +195,6 @@
DW:
UDW:
Nie można zapisać załącznika.
-
- "do "
+
@@ -306,9 +301,6 @@
Uwierzytelnianie…
Pobieranie ustawień konta…
Anulowanie…
- Prawie gotowe!
- Wpisz nazwę (opcjonalną) tego konta:
- Twoje imię i nazwisko (pojawi się w wysyłanych wiadomościach):
Rodzaj konta
Jakiego typu serwer obsługuje to konto?
POP3
@@ -360,7 +352,6 @@
Rodzaj uwierzytelnienia
\"%1$s = %2$s \" nie jest ważny z \"%3$s = %4$s \"
Nieprawidłowe ustawienia: %s
- Opcje konta
Pobieranie wiadomości
Nigdy
Co 15 minut
@@ -380,7 +371,6 @@
Co 36 minut
Co 48 minut
Co 60 minut
- Powiadamiaj o nowej poczcie
Wyświetlaj
10 wiadomości
25 wiadomości
@@ -1029,6 +1019,8 @@ Aby skonfigurować Autocrypt na nowym urządzeniu, należy podążać za wyświe
Tą wiadomość można zachować i użyć w formie kopii zapasowej twojego klucza prywatnego, aby to uczynić należy zachować hasło i przechowywać je w bezpiecznym miejscu.
+
+ do %s
Podczas wysyłania wiadomości wystąpił błąd. Sprawdź połączenie sieciowe oraz adres i konfigurację serwera.
Włącz
Wyłącz
@@ -1105,4 +1097,7 @@ Tą wiadomość można zachować i użyć w formie kopii zapasowej twojego klucz
Nazwa oraz adres e-mail
+ Usuwanie konta…
+
+ nieprzeczytane, %s
diff --git a/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml b/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
index 21bbb4ed422357d5fdec0cdbd14fc9ac78f068a8..2b8f58eb7474d8153310098d6a0533467406f2b8 100644
--- a/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
@@ -28,9 +28,6 @@
Descubra as novidades dessa versão
Ver
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -200,8 +197,8 @@
Cc:
Cco:
Não foi possível salvar o anexo.
-
- "para "
+
+ para %s
+
@@ -309,9 +306,6 @@
Autenticando\u2026
Buscando as configurações da conta\u2026
Cancelando\u2026
- Quase terminado!
- Dê um nome a esta conta (opcional):
- Digite seu nome (será exibido nas mensagens enviadas):
Tipo de conta
Esta conta é de que tipo?
POP3
@@ -363,7 +357,6 @@
Autenticação
\"%1$s = %2$s \" não é válido com \"%3$s = %4$s \"
Configuração inválida: %s
- Opções da conta
Frequência de verificação da pasta
Nunca
A cada 15 minutos
@@ -383,7 +376,6 @@
A cada 36 minutos
A cada 48 minutos
A cada 60 minutos
- Notificar quando chegarem novas mensagens
Número de mensagens a serem exibidas
10 mensagens
25 mensagens
@@ -1103,4 +1095,7 @@ Você pode guardar esta mensagem e usá-la como um backup da sua chave secreta.
Nome e endereço de e-mail
+ Removendo a conta…
+
+ não lidas, 1 %s
diff --git a/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml b/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
index 5928d6c5845bb52a8b5d6c494a68ef5516ae5db2..129f34e70fd7547d80965e3689fce10ea4618130 100644
--- a/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
@@ -28,9 +28,6 @@
Descubra o que há de novo nesta versão
Ver
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -201,8 +198,8 @@
Cc:
Bcc:
Não foi possível guardar o anexo
-
- "para "
+
+ para %s
+
@@ -310,9 +307,6 @@
A autenticar\u2026
A obter configurações da conta\u2026
A cancelar\u2026
- Está quase terminado!
- Dê um nome a esta conta (opcional):
- Introduza o seu nome (é exibido nas mensagens enviadas):
Tipo de conta
Que tipo de conta é esta?
POP3
@@ -364,7 +358,6 @@
Autenticação
\"%1$s = %2$s \" não é válido com \"%3$s = %4$s \"
Configuração inválida: %s
- Opções da conta
Frequência de verificação de pastas
Nunca
A cada 15 minutos
@@ -384,7 +377,6 @@
A cada 36 minutos
A cada 48 minutos
A cada 60 minutos
- Notificar-me na chegada de email
Número de mensagens para exibição
10 mensagens
25 mensagens
@@ -1104,4 +1096,5 @@ Pode manter esta mensagem e usá-la como uma cópia de segurança para a sua cha
Nome e endereço de email
+
diff --git a/app/ui/legacy/src/main/res/values-ro/strings.xml b/app/ui/legacy/src/main/res/values-ro/strings.xml
index 10ccf2d07a7e62a3e28ddd07df015281eab35fe5..ba9d3cfd9881b0b488478678e64011e7d1459812 100644
--- a/app/ui/legacy/src/main/res/values-ro/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ro/strings.xml
@@ -27,9 +27,6 @@
Aflați ce este nou în această versiune
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -200,8 +197,6 @@ cel mult încă %d
Cc:
Bcc:
Nu se poate salva atașamentul.
-
- "la "
+
@@ -305,9 +300,6 @@ cel mult încă %d
Autentifică\u2026
Preluarea setărilor contului\u2026
Se întrerupe\u2026
- Ești aproape gata!
- Dă acestui cont un nume (opțional):
- Tastează numele tău (cum va fi afișat pe mesajele trimise):
Tipul contului
Ce fel de cont este acesta?
POP3
@@ -358,7 +350,6 @@ cel mult încă %d
Autentificare
\"%1$s = %2$s \" nu este valabil cu \"%3$s = %4$s
Configurare greşită : %s
- Opţiuni cont
Frecvență interogare dosar
Niciodată
La fiecare 15 minute
@@ -378,7 +369,6 @@ cel mult încă %d
La fiecare 36 de minute
La fiecare 48 de minute
La fiecare 60 de minute
- Notifică-mă când a venit mail
Număr de mesaje afișate
10 mesaje
25 mesaje
@@ -1024,6 +1014,14 @@ Pentru a seta noul dispozitiv cu Autocript, te rog urmărește indicațiile afi
Poți păstra acest mesaj și să îl folosești drept copie de siguranță a cheii tale secrete. Dacă vrei să faci asta, va trebui să-ți notezi o copie a parolei pe care să o păstrezi undeva în siguranță.
+
+ Vizualizare
+
+ la %s
+
+ Densitate
+ Compact
+ Relaxat
A apărut o eroare la trimiterea mesajului. Te rog verifică conexiunea la rețea și setările serverului de trimitere.
Pornit
Oprit
@@ -1098,5 +1096,8 @@ Poți păstra acest mesaj și să îl folosești drept copie de siguranță a ch
Adresa e-mail
Numele și adresa de e-mail
-
+
+ Se elimină contul…
+
+ necitit, %s
diff --git a/app/ui/legacy/src/main/res/values-ru/strings.xml b/app/ui/legacy/src/main/res/values-ru/strings.xml
index fc412182b5e9599efbdf31edffa81ea74925bc54..cd7743fa9cb2f5cf238e22fef3fd201ce9babf17 100644
--- a/app/ui/legacy/src/main/res/values-ru/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ru/strings.xml
@@ -25,9 +25,6 @@
Что нового
Показывать последние изменения после обновления приложения
Узнайте, что нового в этом выпуске
-
- Добро пожаловать
- Mail — почтовый клиент по умолчанию для /e/OS
-- Отправлено через /e/OS Mail.
@@ -292,9 +289,6 @@
Аутентификация\u2026
Получение настроек\u2026
Отмена\u2026
- Всё почти готово!
- Название учётной записи (необязательно):
- Ваше имя (видно адресату в сообщениях):
Тип учётной записи
Что это за аккаунт\?
POP3
@@ -345,7 +339,6 @@
Аутентификация
\"%1$s = %2$s \" недействителен для \"%3$s = %4$s \"
Неверная настройка: %s
- Настройки учётной записи
Интервал проверки
Вручную
Каждые 15 минут
@@ -365,7 +358,6 @@
36 минут
48 минут
1 час
- Уведомлять о новой почте
Загружать сообщений
10 сообщений
25 сообщений
@@ -1102,8 +1094,6 @@
Показать удаленные изображения
мне
+
- "в "
- Создайте новую учетную запись или импортируйте учетные записи электронной почты из предыдущего файла настроек.
Показать
@@ -1117,4 +1107,5 @@
Написать письмо
+
diff --git a/app/ui/legacy/src/main/res/values-sk/strings.xml b/app/ui/legacy/src/main/res/values-sk/strings.xml
index 305bc613ec527c13b4a6255639fa5f29fd835799..2270642b52edc34515f169c358c22472a55cea80 100644
--- a/app/ui/legacy/src/main/res/values-sk/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sk/strings.xml
@@ -22,9 +22,6 @@
Verzia %s
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -269,9 +266,6 @@
Overovanie\u2026
Načítavanie nastavení účtu\u2026
Rušenie\u2026
- Takmer hotovo!
- Pomenovať tento účet (voliteľné):
- Zadajte vaše meno (zobrazí sa v odchádzajúcich správach):
Typ účtu
Typ účtu
POP3
@@ -320,7 +314,6 @@
Overenie
\"%1$s = %2$s \" nie je platné s \"%3$s = %4$s \"
Neplatné nastavenie: %s
- Možnosti účtu
Frekvencia synchronizácie priečinkov
Nikdy
Každých 15 minút
@@ -340,7 +333,6 @@
Každých 36 minút
Každých 48 minút
Každých 60 minút
- Upozorniť ma, keď príde nová správa
Počet správ na zobrazenie
10 správ
25 správ
@@ -900,4 +892,5 @@
E-mailová adresa
+
diff --git a/app/ui/legacy/src/main/res/values-sl/strings.xml b/app/ui/legacy/src/main/res/values-sl/strings.xml
index 56c77da0db9d3092435f8538c6c245a283e551cf..23eb740255fba1d74399a61003cfa6065958312d 100644
--- a/app/ui/legacy/src/main/res/values-sl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sl/strings.xml
@@ -27,9 +27,6 @@
Kaj je novega v tej izdaji
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -203,7 +200,6 @@ dodatnih %d sporočil
Skp:
Priloge ni mogoče shraniti.
- "za "
+
@@ -307,9 +303,6 @@ dodatnih %d sporočil
Overjanje \u2026
Pridobivanje nastavitev računa \u2026
Poteka preklic \u2026
- Račun je že skoraj pripravljen!
- Prikazano ime računa (izbirno):
- Ime pošiljatelja (prikazano v vseh odhodnih sporočilih):
Vrsta računa
Kakšne vrste je račun?
POP3
@@ -360,7 +353,6 @@ dodatnih %d sporočil
Overjanje
\"%1$s = %2$s \" *ni veljavno* z \"%3$s = %4$s \"
Neveljavna nastavitev: %s
- Možnosti računa
Pogostost izpraševanja map
Ročno preverjanje
Vsakih 15 minut
@@ -380,7 +372,6 @@ dodatnih %d sporočil
Vsakih 36 minut
Vsakih 48 minut
Vsakih 60 minut
- Obveščaj ob prispetju nove pošte
Število prikazanih sporočil
10 sporočil
25 sporočil
@@ -1029,6 +1020,8 @@ Za nastavitev nove naprave za šifriranje AutoCrypt, sledite navodilom, prikazan
Sporočilo lahko shranite in ga uporabite kot varno kopijo šifrirnega ključa. Če boste to storili, si geslo zapišite in ga varno shranite.
+
+ za %s
Prišlo je do napake med pošiljanjem sporočila. Preverite omrežne nastavitve in nastavitve odhodnega strežnika.
Vključi
Izključi
@@ -1104,4 +1097,5 @@ Sporočilo lahko shranite in ga uporabite kot varno kopijo šifrirnega ključa.
Ime in e-poštni naslov
+
diff --git a/app/ui/legacy/src/main/res/values-sq/strings.xml b/app/ui/legacy/src/main/res/values-sq/strings.xml
index 7f359058823f4767831bdd7f01cfb361932b3ca5..1eba3f2e403e40366e4b3789c3c27281d2b6624d 100644
--- a/app/ui/legacy/src/main/res/values-sq/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sq/strings.xml
@@ -28,9 +28,6 @@
Mësoni ç\’ka të re në këtë hedhje në qarkullim
Shiheni
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -200,7 +197,6 @@
Bcc:
S\’arrihet të ruhet bashkëngjitje
- "për "
+
@@ -306,9 +302,6 @@
Po kryhet mirëfilltësimi\u2026
Po sillen rregullime llogarie\u2026
Po anulohet\u2026
- Thuajse mbaruat!
- Jepini një emër kësaj llogarie (në daçi):
- Shtypni emrin tuaj (shfaqet në mesazhet e dërguara):
Lloj llogarie
Ç\’lloj llogarie është kjo?
POP3
@@ -360,7 +353,6 @@
Mirëfilltësim
\“%1$s = %2$s \” s\’është i vlefshëm me \“%3$s = %4$s \”
Rregullim i pavefshëm: %s
- Mundësi llogarie
Shpeshti vjeljeje dosjeje
Kurrë
Çdo 15 minuta
@@ -380,7 +372,6 @@
Çdo 36 minuta
Çdo 48 minuta
Çdo 60 minuta
- Njoftomë kur mbërrin postë
Numër mesazhesh për shfaqje
10 mesazhe
25 mesazhe
@@ -1095,4 +1086,8 @@ Mund ta mbani këtë mesazh dhe ta përdorni si një kopjeruatje të kyçit tuaj
Emër dhe adresë email
+
+ Po hiqet llogaria…
+
+ të palexuar, %s
diff --git a/app/ui/legacy/src/main/res/values-sr/strings.xml b/app/ui/legacy/src/main/res/values-sr/strings.xml
index cb655cc58657dbbc34d7020d3daf092f28d103bb..767db0cb641bd8b3f56916882511a9abc3b41595 100644
--- a/app/ui/legacy/src/main/res/values-sr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sr/strings.xml
@@ -22,9 +22,6 @@
Верзија %s
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -268,9 +265,6 @@
Аутентификујем\u2026
Добављам поставке налога\u2026
Одустајем\u2026
- Скоро сте завршили!
- Именујте овај налог (необавезно):
- Унесите своје име (приказано на одлазним порукама):
Тип налога
Какав је ово налог?
ПОП3
@@ -319,7 +313,6 @@
Аутентификација
„%1$s = %2$s “ није исправно са „%3$s = %4$s “
Неисправна постава: %s
- Опције налога
Проверавање фасцикли
никад
на 15 минута
@@ -339,7 +332,6 @@
на 36 минута
на 48 минута
на 60 минута
- Обавести ме кад стигне пошта
Број порука за приказ
10 порука
25 порука
diff --git a/app/ui/legacy/src/main/res/values-sv/strings.xml b/app/ui/legacy/src/main/res/values-sv/strings.xml
index 66899e828afd22fe3e24504152eea39e35d5c9aa..c0f92d81719b037104e3b91d3801cb1dd458d1a6 100644
--- a/app/ui/legacy/src/main/res/values-sv/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sv/strings.xml
@@ -25,9 +25,6 @@
Vad är nytt
Visa senaste ändringar när appen uppdaterades
Ta reda på vad som är nytt i denna utgåva
-
- Välkommen till E-post
- E-post är standardappen för e-post i /e/OS
-- Skickat från /e/OS E-post.
@@ -294,9 +291,6 @@
Autentiserar\u2026
Hämtar kontoinställningar\u2026
Avbryter\u2026
- Du är nästan klar!
- Ge kontot ett namn (valfritt):
- Ange ditt namn (visas på utgående e-post):
Kontotyp
Vilken typ av konto är detta?
POP3
@@ -312,7 +306,6 @@
Klientcertifikat
POP3-server
IMAP-server
- Exchange-server
Port
Säkerhetstyp
Autentiseringstyp
@@ -338,9 +331,6 @@
Skräppostmapp
Visa endast prenumererade mappar
Expandera automatiskt mapp
- Sökväg för Outlook Web App
- Sökväg för autentisering
- Alias för brevlåda
Inställningar för utgående server
SMTP-server
Port
@@ -351,7 +341,6 @@
Autentiseringstyp
\"%1$s = %2$s \" är inte giltigt med \"%3$s = %4$s \"
Ogiltig konfiguration: %s
- Kontoinställningar
Frekvens för mappkontroll
Aldrig
Var 15:e minut
@@ -371,7 +360,6 @@
Var 36:e minut
Var 48:e minut
Var 60:e minut
- Meddela mig vid ankomst av e-post
Antal meddelanden att visa
10 meddelanden
25 meddelanden
@@ -1008,6 +996,8 @@ För att ställa in din nya enhet för Autocrypt, följ instruktionerna som bör
Du kan behålla detta meddelande och använda det som en säkerhetskopia för din hemliga nyckel. Om du vill göra det ska du skriva ner lösenordet och lagra det säkert.
+
+ till %s
Ett fel uppstod under tiden meddelandet skickades. Kontrollera din nätverksanslutning och utgående serverkonfiguration.
På
Av
@@ -1048,7 +1038,6 @@ Du kan behålla detta meddelande och använda det som en säkerhetskopia för di
Flytta…
Alla meddelanden har lästs in
- Ställ in ett nytt konto eller importera ditt e-postkonto från en tidigare inställningsfil.
FORTSÄTT
AVBRYT
+
@@ -1082,7 +1071,6 @@ Du kan behålla detta meddelande och använda det som en säkerhetskopia för di
Namn och e-postadress
Upphovsmän
Visa
- "till "
Kompakt
Ljud vid skickad e-post
Var 5 minut
@@ -1107,5 +1095,6 @@ Du kan behålla detta meddelande och använda det som en säkerhetskopia för di
\nFör att skicka ett krypterat e-postmeddelande krävs ett nyckelpar så att data krypterad på avsändarens enhet kan avkrypteras av den avsedda mottagaren. Till exempel behöver du den publika nyckeln från den person du skickar till och samma gäller det omvända.
Tar bort konto …
+
olästa, %s
-
\ No newline at end of file
+
diff --git a/app/ui/legacy/src/main/res/values-tr/strings.xml b/app/ui/legacy/src/main/res/values-tr/strings.xml
index b72fa84c3b4d5fa30e2af6a7713961c7f0af41a3..8625a05816b4c4db7691cd73b111bbf841a81721 100644
--- a/app/ui/legacy/src/main/res/values-tr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-tr/strings.xml
@@ -27,9 +27,6 @@
Bu sürümdeki yenilikleri öğrenin
-
- Mail Posta\'ya Hoşgeldiniz
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -295,9 +292,6 @@
Kimlik Doğrulaması\u2026
Hesap ayarları alınıyor\u2026
İptal ediliyor\u2026
- Hemen hemen tamam!
- Bu hesaba bir isim verin (isteğe bağlı):
- İsminizi yazın (Giden mesajlarda görüntülenir):
Hesap türü
Bu ne tür bir hesaptır?
POP3
@@ -347,7 +341,6 @@
Kimlik Doğrulama
\"%1$s = %2$s \" bununla uyuşmuyor \"%3$s = %4$s \"
Geçersiz kurulum: %s
- Hesap seçenekleri
Klasör bilgi toplama sıklığı
Asla
Her 15 dakikada bir
@@ -367,7 +360,6 @@
Her 36 dakikada bir
Her 48 dakikada bir
Her 60 dakikada bir
- Posta ulaştığında beni uyar
Görüntülenecek ileti sayısı
10 ileti
25 ileti
diff --git a/app/ui/legacy/src/main/res/values-uk/strings.xml b/app/ui/legacy/src/main/res/values-uk/strings.xml
index 998cdc2ff090114c3c19c65f55bd705c77faf1ed..54687ca37e71d32a4e3ca8d9f7cc6615654ba56a 100644
--- a/app/ui/legacy/src/main/res/values-uk/strings.xml
+++ b/app/ui/legacy/src/main/res/values-uk/strings.xml
@@ -27,9 +27,6 @@
Дізнатися, що нового у цьому релізі
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -303,9 +300,6 @@
Автентифікація\u2026
Отримання налаштувань облікового запису\u2026
Скасування\u2026
- Майже готово!
- Дайте ім’я цьому обліковому запису (необов’язково):
- Введіть ваше ім’я (відображається у вихідних повідомленнях):
Тип облікового запису
Виберіть тип поштової скриньки
POP3
@@ -356,7 +350,6 @@
Метод автентифікації
\"%1$s = %2$s \" недійсне з \"%3$s = %4$s \"
Неправильне налаштування: %s
- Параметри облікового запису
Частота опитування тек
Ніколи
Кожні 15 хвилин
@@ -376,7 +369,6 @@
Кожні 36 хвилин
Кожні 48 хвилин
Кожні 60 хвилин
- Сповіщати мене про отримання нової пошти
Кількість відображуваних повідомлень
10 повідомлень
25 повідомлень
@@ -1024,10 +1016,11 @@
Ви можете зберегти це повідомлення і використовувати його як резервну копію вашого секретного ключа. Якщо ви хочете це зробити, запишіть пароль і збережіть його у надійному місці.
+
+ для %s
Показати
- "для "
+
@@ -1077,7 +1070,7 @@
Спам
- Перемістити...
+ Перемістити…
@@ -1126,4 +1119,8 @@
Ім\'я та адреса ел. пошти
+
+ Видалення облікового запису…
+
+ Непрочитані, %s
diff --git a/app/ui/legacy/src/main/res/values-v26/drawables.xml b/app/ui/legacy/src/main/res/values-v26/drawables.xml
deleted file mode 100644
index ecf5f57c446638efe6f16fc8acf1d229cf6085f3..0000000000000000000000000000000000000000
--- a/app/ui/legacy/src/main/res/values-v26/drawables.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- @mipmap/ic_e_launcher
-
diff --git a/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml b/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
index 8e88f1545ad55674d14e50238ac77ad8c4273e27..207db126428359195884dd8ec9aa15a361d77698 100644
--- a/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
+++ b/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
@@ -28,9 +28,6 @@
了解此版本的新增功能
查看
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -196,8 +193,8 @@
抄送:
密送:
无法保存附件。
-
- 到
+
+ 到%s
+
@@ -305,9 +302,6 @@
正在验证用户\u2026
正在获取账户设置\u2026
正在取消\u2026
- 快要完成了!
- 为这个账户选择一个名称(可选项):
- 输入你的名字(将显示在发出的邮件中):
账户类型
这个账户是什么类型的?
POP3
@@ -359,7 +353,6 @@
身份验证方法
\"%1$s = %2$s \" 对于 \"%3$s = %4$s \" 无效
无效的设置:%s
- 账户选项
文件夹检查频率
从不
15 分钟一次
@@ -379,7 +372,6 @@
36 分钟一次
48 分钟一次
60 分钟一次
- 有新邮件时通知我
邮件显示数量
10 封
25 封
@@ -1089,4 +1081,7 @@
姓名和电子邮箱地址
+ 删除账户中…
+
+ 未读,%s
diff --git a/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml b/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
index 1695bd19fb49f0d91429fd8a17088b226e49e64a..b068829e3c5f1d4fa7389356b4656eef31373239 100644
--- a/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
+++ b/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
@@ -28,9 +28,6 @@
了解此版本的新增功能
檢視
-
- Welcome to Mail
- Mail is the default mail client for /e/
-- Sent from /e/ Mail.
@@ -190,8 +187,8 @@
副本:
密件副本:
無法存檔附件。
-
- 至
+
+ 至%s
+
@@ -300,9 +297,6 @@
驗證…
正在收取帳號設定…
正在取消…
- 快要完成了!
- 為這個帳號選擇一個名稱(非必填):
- 輸入你的名字(將顯示在發出的郵件中):
帳號類型
這個帳號是什麼類型的?
POP3
@@ -354,7 +348,6 @@
身份驗證方法
「%1$s = %2$s 」對於「%3$s = %4$s 」無效
無效的設定:%s
- 帳號選項
信件匣檢查頻率
不要
15 分鐘一次
@@ -374,7 +367,6 @@
36 分鐘一次
48 分鐘一次
60 分鐘一次
- 有新郵件時通知我
郵件顯示數量
10 封
25 封
@@ -1079,4 +1071,5 @@
姓名和電子郵件地址
+
diff --git a/app/ui/legacy/src/main/res/values/app_logo_colors.xml b/app/ui/legacy/src/main/res/values/app_logo_colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9642c4fa2f04cb15f61b75bd54ea49eeefbb1990
--- /dev/null
+++ b/app/ui/legacy/src/main/res/values/app_logo_colors.xml
@@ -0,0 +1,8 @@
+
+
+ #ff1744
+ #ff4569
+ #d81a3d
+
+ #ffdae1
+
diff --git a/app/ui/legacy/src/main/res/values/attrs.xml b/app/ui/legacy/src/main/res/values/attrs.xml
index 128f086272ecd1da20ec11e06393fb05455d7bcd..a1b7dd4d1555ce3a8a162fce48d9157202f78505 100644
--- a/app/ui/legacy/src/main/res/values/attrs.xml
+++ b/app/ui/legacy/src/main/res/values/attrs.xml
@@ -110,7 +110,6 @@
-
diff --git a/app/ui/legacy/src/main/res/values/colors.xml b/app/ui/legacy/src/main/res/values/colors.xml
index c35ea2f55018befe20550fd94626c16977eb4743..fd221c0ef1a1da9580137844567e368e1506ec5e 100644
--- a/app/ui/legacy/src/main/res/values/colors.xml
+++ b/app/ui/legacy/src/main/res/values/colors.xml
@@ -15,8 +15,6 @@
-->
- #FFDAE1
-
#444444
#888
diff --git a/app/ui/legacy/src/main/res/values/drawables.xml b/app/ui/legacy/src/main/res/values/drawables.xml
index 1448b53a1873f6ba8fbc99756229f3e91a4a8b27..f5a91e6d534576a0f4f70adc79ac1e5a9162cd79 100644
--- a/app/ui/legacy/src/main/res/values/drawables.xml
+++ b/app/ui/legacy/src/main/res/values/drawables.xml
@@ -1,4 +1,4 @@
- @mipmap/icon_e
+ @drawable/ic_app_logo
diff --git a/app/ui/legacy/src/main/res/values/strings.xml b/app/ui/legacy/src/main/res/values/strings.xml
index 2e4cb4ae547265dba391edc00d9961f065f9ee77..bfa7fa35ccc48b8832fe145f53a630aec827c634 100644
--- a/app/ui/legacy/src/main/res/values/strings.xml
+++ b/app/ui/legacy/src/main/res/values/strings.xml
@@ -30,10 +30,6 @@
View
-
- Welcome to Mail
- Set up a new account or import your email accounts from a previous settings file.
- Mail is the default mail client for /e/OS
-- Sent from /e/OS Mail.
@@ -205,8 +201,8 @@
Bcc:
Unable to save attachment.
-
- "to "
+
+ to %s
+
@@ -321,9 +317,7 @@
Authenticating\u2026
Fetching account settings\u2026
Canceling\u2026
- You\'re almost done!
- Give this account a name (optional):
- Type your name (displays on outgoing messages):
+
Account type
What kind of account is this?
POP3
@@ -377,7 +371,8 @@
Authentication
\"%1$s = %2$s \" is not valid with \"%3$s = %4$s \"
Invalid setup: %s
- Account options
+
+
Folder poll frequency
Never
Every 5 minutes
@@ -398,7 +393,8 @@
Every 36 minutes
Every 48 minutes
Every 60 minutes
- Notify me when mail arrives
+
+
Number of messages to display
10 messages
25 messages
diff --git a/app/ui/legacy/src/main/res/values/styles.xml b/app/ui/legacy/src/main/res/values/styles.xml
index 2076e406c0e852668373fb4a42adc9e4b09d3ac3..5461dbb512562b6f1b159155e26cea11e570d105 100644
--- a/app/ui/legacy/src/main/res/values/styles.xml
+++ b/app/ui/legacy/src/main/res/values/styles.xml
@@ -58,19 +58,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt b/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt
index b9d1a3c31e236bafa563ab2b7d72a0c67b33681b..14a306ab731c09a275c3ee040113e1ad52409318 100644
--- a/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt
+++ b/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt
@@ -24,7 +24,7 @@ class RecipientLayoutCreatorTest : RobolectricTest() {
private val recipientLayoutCreator = RecipientLayoutCreator(
textMeasure = textMeasure,
maxNumberOfRecipientNames = 5,
- recipientsPrefix = "to ",
+ recipientsFormat = "to %s",
additionalRecipientSpacing = 1,
additionalRecipientsPrefix = "+",
)
diff --git a/backend/demo/src/main/resources/inbox/intro.eml b/backend/demo/src/main/resources/inbox/intro.eml
index 7ce770d31b2168fb8b0bdbb26ecbd838fa3dd771..ae5ed699e65de504d6ce19fb5fab0d66126845c6 100644
--- a/backend/demo/src/main/resources/inbox/intro.eml
+++ b/backend/demo/src/main/resources/inbox/intro.eml
@@ -1,9 +1,9 @@
-MIME-Version: 1.0
-From: "cketti"
-Date: Thu, 23 Sep 2021 23:42:00 +0200
-Message-ID:
-Subject: Welcome to Mail
-To: User
-Content-Type: text/plain; charset=UTF-8
-
-Congratulations, you have managed to set up Mail's demo account. Have fun exploring the app.
+MIME-Version: 1.0
+From: "eOS"
+Date: Thu, 23 Sep 2021 23:42:00 +0200
+Message-ID:
+Subject: Welcome to Mail
+To: User
+Content-Type: text/plain; charset=UTF-8
+
+Congratulations, you have managed to set up Mail's demo account. Have fun exploring the app.
diff --git a/backend/demo/src/main/resources/turing/turing_award_1966.eml b/backend/demo/src/main/resources/turing/turing_award_1966.eml
index 6ad832041daf3701527e3d9cfc6d0557d954821b..bed070890c88bd17b08f5b792da9edb5029ac474 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1966.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1966.eml
@@ -1,84 +1,84 @@
-MIME-Version: 1.0
-From: "Alan J. Perlis"
-Date: Sat, 01 Jan 1966 12:00:00 -0400
-Message-ID:
-Subject: The Synthesis of Algorithmic Systems
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7b450b100959e604d85a5320
-
---047d7b450b100959e604d85a5320
-Content-Type: text/plain; charset=UTF-8
-
-Both knowledge and wisdom extend man's reach. Knowledge led to computers,
-wisdom to chopsticks. Unfortunately our association is overinvolved with
-the former. The latter will have to wait for a more sublime day.
-On what does and will the fame of Turing rest? That he proved a theorem
-showing that for a general computing device--later dubbed a "Turing
-machine"--there existed functions which it could not compute? I doubt it.
-More likely it rests on the model he invented and employed: his formal
-mechanism.
-This model has captured the imagination and mobilized the thoughts of a
-generation of scientists. It has provided a basis for arguments leading to
-theories. His model has proved so useful that its generated activity has
-been distributed not only in mathematics, but through several technologies
-as well. The arguments that have been employed are not always formal and
-the consequent creations not all abstract.
-Indeed a most fruitful consequence of the Turing machine has been with the
-creation, study and computation of functions which are computable, i.e., in
-computer programming. This is not surprising since computers can compute so
-much more than we yet know how to specify.
-I am sure that all will agree that this model has been enormously valuable.
-History will forgive me for not devoting any attention in this lecture to
-the effect which Turing had on the development of the general-purpose
-digital computer, which has further accelerated our involvement with the
-theory and practice of computation.
-Since the appearance of Turing's model there have, of course, been others
-which have concerned and benefited us in computing. I think, however, that
-only one has had an effect as great as Turing's: the formal mechanism
-called ALGOL Many will immediately disagree, pointing out that too few of
-us have understood it or used it.
-While such has, unhappily, been the case, it is not the point. The impulse
-given by ALGOL to the development of research in computer science is
-relevant while the number of adherents is not. ALGOL, too, has mobilized
-our thoughts and has provided us with a basis for our arguments.
-
---047d7b450b100959e604d85a5320
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-Both knowledge and wisdom extend man's reach. Kno=
-wledge led to computers, wisdom to chopsticks. Unfortunately our associatio=
-n is overinvolved with the former. The latter will have to wait for a more =
-sublime day.=C2=A0
-On what does and will the fame of Turing rest? That he proved a theore=
-m showing that for a general computing device--later dubbed a "Turing =
-machine"--there existed functions which it could not compute? I doubt =
-it. More likely it rests on the model he invented and employed: his formal =
-mechanism.=C2=A0
-This model has captured the imagination and mobilized the thoughts of =
-a generation of scientists. It has provided a basis for arguments leading t=
-o theories. His model has proved so useful that its generated activity has =
-been distributed not only in mathematics, but through several technologies =
-as well. The arguments that have been employed are not always formal and th=
-e consequent creations not all abstract.=C2=A0
-Indeed a most fruitful consequence of the Turing machine has been with=
- the creation, study and computation of functions which are computable, i.e=
-., in computer programming. This is not surprising since computers can comp=
-ute so much more than we yet know how to specify.=C2=A0
-I am sure that all will agree that this model has been enormously valu=
-able. History will forgive me for not devoting any attention in this lectur=
-e to the effect which Turing had on the development of the general-purpose =
-digital computer, which has further accelerated our involvement with the th=
-eory and practice of computation.=C2=A0
-Since the appearance of Turing's model there have, of course, been=
- others which have concerned and benefited us in computing. I think, howeve=
-r, that only one has had an effect as great as Turing's: the formal mec=
-hanism called ALGOL Many will immediately disagree, pointing out that too f=
-ew of us have understood it or used it.=C2=A0
-While such has, unhappily, been the case, it is not the point. The imp=
-ulse given by ALGOL to the development of research in computer science is r=
-elevant while the number of adherents is not. ALGOL, too, has mobilized our=
- thoughts and has provided us with a basis for our arguments.=C2=A0
-
-
---047d7b450b100959e604d85a5320--
+MIME-Version: 1.0
+From: "Alan J. Perlis"
+Date: Sat, 01 Jan 1966 12:00:00 -0400
+Message-ID:
+Subject: The Synthesis of Algorithmic Systems
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7b450b100959e604d85a5320
+
+--047d7b450b100959e604d85a5320
+Content-Type: text/plain; charset=UTF-8
+
+Both knowledge and wisdom extend man's reach. Knowledge led to computers,
+wisdom to chopsticks. Unfortunately our association is overinvolved with
+the former. The latter will have to wait for a more sublime day.
+On what does and will the fame of Turing rest? That he proved a theorem
+showing that for a general computing device--later dubbed a "Turing
+machine"--there existed functions which it could not compute? I doubt it.
+More likely it rests on the model he invented and employed: his formal
+mechanism.
+This model has captured the imagination and mobilized the thoughts of a
+generation of scientists. It has provided a basis for arguments leading to
+theories. His model has proved so useful that its generated activity has
+been distributed not only in mathematics, but through several technologies
+as well. The arguments that have been employed are not always formal and
+the consequent creations not all abstract.
+Indeed a most fruitful consequence of the Turing machine has been with the
+creation, study and computation of functions which are computable, i.e., in
+computer programming. This is not surprising since computers can compute so
+much more than we yet know how to specify.
+I am sure that all will agree that this model has been enormously valuable.
+History will forgive me for not devoting any attention in this lecture to
+the effect which Turing had on the development of the general-purpose
+digital computer, which has further accelerated our involvement with the
+theory and practice of computation.
+Since the appearance of Turing's model there have, of course, been others
+which have concerned and benefited us in computing. I think, however, that
+only one has had an effect as great as Turing's: the formal mechanism
+called ALGOL Many will immediately disagree, pointing out that too few of
+us have understood it or used it.
+While such has, unhappily, been the case, it is not the point. The impulse
+given by ALGOL to the development of research in computer science is
+relevant while the number of adherents is not. ALGOL, too, has mobilized
+our thoughts and has provided us with a basis for our arguments.
+
+--047d7b450b100959e604d85a5320
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+Both knowledge and wisdom extend man's reach. Kno=
+wledge led to computers, wisdom to chopsticks. Unfortunately our associatio=
+n is overinvolved with the former. The latter will have to wait for a more =
+sublime day.=C2=A0
+On what does and will the fame of Turing rest? That he proved a theore=
+m showing that for a general computing device--later dubbed a "Turing =
+machine"--there existed functions which it could not compute? I doubt =
+it. More likely it rests on the model he invented and employed: his formal =
+mechanism.=C2=A0
+This model has captured the imagination and mobilized the thoughts of =
+a generation of scientists. It has provided a basis for arguments leading t=
+o theories. His model has proved so useful that its generated activity has =
+been distributed not only in mathematics, but through several technologies =
+as well. The arguments that have been employed are not always formal and th=
+e consequent creations not all abstract.=C2=A0
+Indeed a most fruitful consequence of the Turing machine has been with=
+ the creation, study and computation of functions which are computable, i.e=
+., in computer programming. This is not surprising since computers can comp=
+ute so much more than we yet know how to specify.=C2=A0
+I am sure that all will agree that this model has been enormously valu=
+able. History will forgive me for not devoting any attention in this lectur=
+e to the effect which Turing had on the development of the general-purpose =
+digital computer, which has further accelerated our involvement with the th=
+eory and practice of computation.=C2=A0
+Since the appearance of Turing's model there have, of course, been=
+ others which have concerned and benefited us in computing. I think, howeve=
+r, that only one has had an effect as great as Turing's: the formal mec=
+hanism called ALGOL Many will immediately disagree, pointing out that too f=
+ew of us have understood it or used it.=C2=A0
+While such has, unhappily, been the case, it is not the point. The imp=
+ulse given by ALGOL to the development of research in computer science is r=
+elevant while the number of adherents is not. ALGOL, too, has mobilized our=
+ thoughts and has provided us with a basis for our arguments.=C2=A0
+
+
+--047d7b450b100959e604d85a5320--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1967.eml b/backend/demo/src/main/resources/turing/turing_award_1967.eml
index a7e3299e195a22ab56a257d5e629214e88796218..9326afab9f89d7135d03b97c2a2a32b4dd503255 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1967.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1967.eml
@@ -1,35 +1,35 @@
-MIME-Version: 1.0
-From: "Maurice V. Wilkes"
-Date: Wed, 30 Aug 1967 12:00:00 -0400
-Message-ID:
-Subject: Computers Then and Now
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7b5d9bdd0d571a04d85aec30
-
---047d7b5d9bdd0d571a04d85aec30
-Content-Type: text/plain; charset=UTF-8
-
-I do not imagine that many of the Turing lecturers who will follow me will
-be people who were acquainted with Alan Turing. The work on computable
-numbers, for which he is famous, was published in 1936 before digital
-computers existed. Later he became one of the first of a distinguished
-succession of able mathematicians who have made contributions to the
-computer field. He was a colorful figure in the early days of digital
-computer development in England, and I would find it difficult to speak of
-that period without making some references to him.
-
---047d7b5d9bdd0d571a04d85aec30
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-I do not imagine that many of the Turing lecturers wh=
-o will follow me will be people who were acquainted with Alan Turing. The w=
-ork on computable numbers, for which he is famous, was published in 1936 be=
-fore digital computers existed. Later he became one of the first of a disti=
-nguished succession of able mathematicians who have made contributions to t=
-he computer field. He was a colorful figure in the early days of digital co=
-mputer development in England, and I would find it difficult to speak of th=
-at period without making some references to him.
-
-
---047d7b5d9bdd0d571a04d85aec30--
+MIME-Version: 1.0
+From: "Maurice V. Wilkes"
+Date: Wed, 30 Aug 1967 12:00:00 -0400
+Message-ID:
+Subject: Computers Then and Now
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7b5d9bdd0d571a04d85aec30
+
+--047d7b5d9bdd0d571a04d85aec30
+Content-Type: text/plain; charset=UTF-8
+
+I do not imagine that many of the Turing lecturers who will follow me will
+be people who were acquainted with Alan Turing. The work on computable
+numbers, for which he is famous, was published in 1936 before digital
+computers existed. Later he became one of the first of a distinguished
+succession of able mathematicians who have made contributions to the
+computer field. He was a colorful figure in the early days of digital
+computer development in England, and I would find it difficult to speak of
+that period without making some references to him.
+
+--047d7b5d9bdd0d571a04d85aec30
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+I do not imagine that many of the Turing lecturers wh=
+o will follow me will be people who were acquainted with Alan Turing. The w=
+ork on computable numbers, for which he is famous, was published in 1936 be=
+fore digital computers existed. Later he became one of the first of a disti=
+nguished succession of able mathematicians who have made contributions to t=
+he computer field. He was a colorful figure in the early days of digital co=
+mputer development in England, and I would find it difficult to speak of th=
+at period without making some references to him.
+
+
+--047d7b5d9bdd0d571a04d85aec30--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1968.eml b/backend/demo/src/main/resources/turing/turing_award_1968.eml
index 274260c0ee707587afee28b0b5fae77335d9ffa7..f9b989ad4126f67beb85bb29abcb1a7424526bb3 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1968.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1968.eml
@@ -1,40 +1,40 @@
-MIME-Version: 1.0
-From: Richard Hamming
-Date: Tue, 27 Aug 1968 12:00:00 -0400
-Message-ID:
-Subject: One Man's View of Computer Science
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=089e01227b30f6f60004d85af2ae
-
---089e01227b30f6f60004d85af2ae
-Content-Type: text/plain; charset=UTF-8
-
-Let me begin with a few personal words. When one is notified that he has
-been elected the ACM Turing lecturer for the year, he is at first
-surprised--especially is the nonacademic person surprised by an ACM award.
-After a little while the surprise is replaced by a feeling of pleasure.
-Still later comes a feeling of "Why me?" With all that has been done and is
-being done in computing, why single out me and my work? Well, I suppose
-that it has to happen to someone each year, and this
-time I am the lucky person. Anyway, let me thank you for the honor you have
-given to me and by inference to the Bell Telephone Laboratories where I
-work and which has made possible so much of what I have done.
-
---089e01227b30f6f60004d85af2ae
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-Let me begin with a few personal words. When one is n=
-otified that he has been elected the ACM Turing lecturer for the year, he i=
-s at first surprised--especially is the nonacademic person surprised by an =
-ACM award. After a little while the surprise is replaced by a feeling of pl=
-easure. Still later comes a feeling of "Why me?" With all that ha=
-s been done and is being done in computing, why single out me and my work? =
-Well, I suppose that it has to happen to someone each year, and this=C2=A0<=
-/div>
-time I am the lucky person. Anyway, let me thank you for the honor you=
- have given to me and by inference to the Bell Telephone Laboratories where=
- I work and which has made possible so much of what I have done.
-
---089e01227b30f6f60004d85af2ae--
+MIME-Version: 1.0
+From: Richard Hamming
+Date: Tue, 27 Aug 1968 12:00:00 -0400
+Message-ID:
+Subject: One Man's View of Computer Science
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=089e01227b30f6f60004d85af2ae
+
+--089e01227b30f6f60004d85af2ae
+Content-Type: text/plain; charset=UTF-8
+
+Let me begin with a few personal words. When one is notified that he has
+been elected the ACM Turing lecturer for the year, he is at first
+surprised--especially is the nonacademic person surprised by an ACM award.
+After a little while the surprise is replaced by a feeling of pleasure.
+Still later comes a feeling of "Why me?" With all that has been done and is
+being done in computing, why single out me and my work? Well, I suppose
+that it has to happen to someone each year, and this
+time I am the lucky person. Anyway, let me thank you for the honor you have
+given to me and by inference to the Bell Telephone Laboratories where I
+work and which has made possible so much of what I have done.
+
+--089e01227b30f6f60004d85af2ae
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+Let me begin with a few personal words. When one is n=
+otified that he has been elected the ACM Turing lecturer for the year, he i=
+s at first surprised--especially is the nonacademic person surprised by an =
+ACM award. After a little while the surprise is replaced by a feeling of pl=
+easure. Still later comes a feeling of "Why me?" With all that ha=
+s been done and is being done in computing, why single out me and my work? =
+Well, I suppose that it has to happen to someone each year, and this=C2=A0<=
+/div>
+time I am the lucky person. Anyway, let me thank you for the honor you=
+ have given to me and by inference to the Bell Telephone Laboratories where=
+ I work and which has made possible so much of what I have done.
+
+--089e01227b30f6f60004d85af2ae--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1970.eml b/backend/demo/src/main/resources/turing/turing_award_1970.eml
index 811130ac1dd71b5e6a5b7487a10c388861e75dad..98edc18e6dd7c6b238864cac2eb38283af77c63d 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1970.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1970.eml
@@ -1,35 +1,35 @@
-MIME-Version: 1.0
-From: "James H. Wilkinson"
-Date: Tue, 01 Sep 1970 12:00:00 -0400
-Message-ID:
-Subject: Some Comments from a Numerical Analyst
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7b5d9bdd9697d504d85ac65f
-
---047d7b5d9bdd9697d504d85ac65f
-Content-Type: text/plain; charset=UTF-8
-
-When at last I recovered from the feeling of shocked elation at being
-invited to give the 1970 Turing Award Lecture, I became aware that I must
-indeed prepare an appropriate lecture. There appears to be a tradition that
-a Turing Lecturer should decide for himself what is expected from him, and
-probably for this reason previous lectures have differed considerably in
-style and content. However, it was made quite clear that I was to give an
-after-luncheon speech and that I would not have the benefit of an overhead
-projector or a blackboard.
-
---047d7b5d9bdd9697d504d85ac65f
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-When at last I recovered from the feeling of shocked =
-elation at being invited to give the 1970 Turing Award Lecture, I became aw=
-are that I must indeed prepare an appropriate lecture. There appears to be =
-a tradition that a Turing Lecturer should decide for himself what is expect=
-ed from him, and probably for this reason previous lectures have differed c=
-onsiderably in style and content. However, it was made quite clear that I w=
-as to give an after-luncheon speech and that I would not have the benefit o=
-f an overhead projector or a blackboard.
-
-
---047d7b5d9bdd9697d504d85ac65f--
+MIME-Version: 1.0
+From: "James H. Wilkinson"
+Date: Tue, 01 Sep 1970 12:00:00 -0400
+Message-ID:
+Subject: Some Comments from a Numerical Analyst
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7b5d9bdd9697d504d85ac65f
+
+--047d7b5d9bdd9697d504d85ac65f
+Content-Type: text/plain; charset=UTF-8
+
+When at last I recovered from the feeling of shocked elation at being
+invited to give the 1970 Turing Award Lecture, I became aware that I must
+indeed prepare an appropriate lecture. There appears to be a tradition that
+a Turing Lecturer should decide for himself what is expected from him, and
+probably for this reason previous lectures have differed considerably in
+style and content. However, it was made quite clear that I was to give an
+after-luncheon speech and that I would not have the benefit of an overhead
+projector or a blackboard.
+
+--047d7b5d9bdd9697d504d85ac65f
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+When at last I recovered from the feeling of shocked =
+elation at being invited to give the 1970 Turing Award Lecture, I became aw=
+are that I must indeed prepare an appropriate lecture. There appears to be =
+a tradition that a Turing Lecturer should decide for himself what is expect=
+ed from him, and probably for this reason previous lectures have differed c=
+onsiderably in style and content. However, it was made quite clear that I w=
+as to give an after-luncheon speech and that I would not have the benefit o=
+f an overhead projector or a blackboard.
+
+
+--047d7b5d9bdd9697d504d85ac65f--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1971.eml b/backend/demo/src/main/resources/turing/turing_award_1971.eml
index 387f995770901c6ce0706a62f7f297833912e23a..00b5b495e0a91bb35c52dbf058bf985e08c9510d 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1971.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1971.eml
@@ -1,32 +1,32 @@
-MIME-Version: 1.0
-From: John McCarthy
-Date: Fri, 01 Jan 1971 12:00:00 -0400
-Message-ID:
-Subject: Generality in Artificial Intelligence
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=089e01030106b6942904d85ad870
-
---089e01030106b6942904d85ad870
-Content-Type: text/plain; charset=UTF-8
-
-Postscript
-My 1971 Turing Award Lecture was entitled "Generality in Artificial
-Intelligence." The topic turned out to have been overambitious in that I
-discovered that I was unable to put my thoughts on the subject in a
-satisfactory written form at that time. It would have been better to have
-reviewed previous work rather than attempt something new, but such wasn't
-my custom at that time.
-
---089e01030106b6942904d85ad870
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-PostscriptMy 1971 Turing Award Lecture was=
- entitled "Generality in Artificial Intelligence." The topic turn=
-ed out to have been overambitious in that I discovered that I was unable to=
- put my thoughts on the subject in a satisfactory written form at that time=
-. It would have been better to have reviewed previous work rather than atte=
-mpt something new, but such wasn't my custom at that time.
-
-
---089e01030106b6942904d85ad870--
+MIME-Version: 1.0
+From: John McCarthy
+Date: Fri, 01 Jan 1971 12:00:00 -0400
+Message-ID:
+Subject: Generality in Artificial Intelligence
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=089e01030106b6942904d85ad870
+
+--089e01030106b6942904d85ad870
+Content-Type: text/plain; charset=UTF-8
+
+Postscript
+My 1971 Turing Award Lecture was entitled "Generality in Artificial
+Intelligence." The topic turned out to have been overambitious in that I
+discovered that I was unable to put my thoughts on the subject in a
+satisfactory written form at that time. It would have been better to have
+reviewed previous work rather than attempt something new, but such wasn't
+my custom at that time.
+
+--089e01030106b6942904d85ad870
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+PostscriptMy 1971 Turing Award Lecture was=
+ entitled "Generality in Artificial Intelligence." The topic turn=
+ed out to have been overambitious in that I discovered that I was unable to=
+ put my thoughts on the subject in a satisfactory written form at that time=
+. It would have been better to have reviewed previous work rather than atte=
+mpt something new, but such wasn't my custom at that time.
+
+
+--089e01030106b6942904d85ad870--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1972.eml b/backend/demo/src/main/resources/turing/turing_award_1972.eml
index 9c533bdbd980523196e57ab8c84930c4e9175472..676c77c0045de540ff0ec314155a5a84f9dc736c 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1972.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1972.eml
@@ -1,27 +1,27 @@
-MIME-Version: 1.0
-From: "Edsger W. Dijkstra"
-Date: Mon, 02 Aug 1972 12:00:00 -0500
-Message-ID:
-Subject: The Humble Programmer
-To: Alan Turing
-Content-Type: text/plain; charset=UTF-8; format=flowed
-
-As a result of a long sequence of coincidences I entered the programming
-profession officially on the first spring morning of 1952, and as far as
-I have been able to trace, I was the first Dutchman to do so in my
-country. In retrospect the most amazing thing is the slowness with which,
-at least in my part of the world, the programming profession emerged, a
-slowness which is now hard to believe. But I am grateful for two vivid
-recollections from that period that establish that slowness beyond any
-doubt.
-
-After having programmed for some three years, I had a discussion with
-van Wijngaarden, who was then my boss at the Mathematical Centre in
-Amsterdam - a discussion for which I shall remain grateful to him
-as long as I live. The point was that I was supposed to study theoretical
-physics at the University of Leiden simultaneously, and as I found the
-two activities harder and harder to combine, I had to make up my
-mind, either to stop programming and become a real, respectable theoretical
-physicist, or to carry my study of physics to a formal completion only,
-with a minimum of effort, and to become..., yes what? A programmer?
-But was that a respectable profession? After all, what was programming?
+MIME-Version: 1.0
+From: "Edsger W. Dijkstra"
+Date: Mon, 02 Aug 1972 12:00:00 -0500
+Message-ID:
+Subject: The Humble Programmer
+To: Alan Turing
+Content-Type: text/plain; charset=UTF-8; format=flowed
+
+As a result of a long sequence of coincidences I entered the programming
+profession officially on the first spring morning of 1952, and as far as
+I have been able to trace, I was the first Dutchman to do so in my
+country. In retrospect the most amazing thing is the slowness with which,
+at least in my part of the world, the programming profession emerged, a
+slowness which is now hard to believe. But I am grateful for two vivid
+recollections from that period that establish that slowness beyond any
+doubt.
+
+After having programmed for some three years, I had a discussion with
+van Wijngaarden, who was then my boss at the Mathematical Centre in
+Amsterdam - a discussion for which I shall remain grateful to him
+as long as I live. The point was that I was supposed to study theoretical
+physics at the University of Leiden simultaneously, and as I found the
+two activities harder and harder to combine, I had to make up my
+mind, either to stop programming and become a real, respectable theoretical
+physicist, or to carry my study of physics to a formal completion only,
+with a minimum of effort, and to become..., yes what? A programmer?
+But was that a respectable profession? After all, what was programming?
diff --git a/backend/demo/src/main/resources/turing/turing_award_1975.eml b/backend/demo/src/main/resources/turing/turing_award_1975.eml
index f91e9ec5c6e010e8d8dc936c3592bf10c5889dc0..c5c835e8fc0abface9c8165c9dfc5bc90351c4a6 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1975.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1975.eml
@@ -1,30 +1,30 @@
-MIME-Version: 1.0
-From: Allen Newell
-Cc: Herbert Simon
-Date: Mon, 20 Oct 1975 12:00:00 -0500
-Message-ID:
-Subject: Computer Science as Empirical Inquiry: Symbols and Search
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7b450b1092035304d85abf33
-
---047d7b450b1092035304d85abf33
-Content-Type: text/plain; charset=UTF-8
-
-Computer science is the study of the phenomena surrounding computers. The
-founders of this society understood this very well when they called
-themselves the Association for Computing Machinery. The machine---not just
-the hardware, but the programmed, living machine--is the organism we study.
-
---047d7b450b1092035304d85abf33
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-Computer science is the study of the phenomena surrounding=
- computers. The founders of this society understood this very well when the=
-y called themselves the Association for Computing Machinery. The machine---=
-not just the hardware, but the programmed, living machine--is the organism =
-we study.
-
-
-
---047d7b450b1092035304d85abf33--
+MIME-Version: 1.0
+From: Allen Newell
+Cc: Herbert Simon
+Date: Mon, 20 Oct 1975 12:00:00 -0500
+Message-ID:
+Subject: Computer Science as Empirical Inquiry: Symbols and Search
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7b450b1092035304d85abf33
+
+--047d7b450b1092035304d85abf33
+Content-Type: text/plain; charset=UTF-8
+
+Computer science is the study of the phenomena surrounding computers. The
+founders of this society understood this very well when they called
+themselves the Association for Computing Machinery. The machine---not just
+the hardware, but the programmed, living machine--is the organism we study.
+
+--047d7b450b1092035304d85abf33
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+Computer science is the study of the phenomena surrounding=
+ computers. The founders of this society understood this very well when the=
+y called themselves the Association for Computing Machinery. The machine---=
+not just the hardware, but the programmed, living machine--is the organism =
+we study.
+
+
+
+--047d7b450b1092035304d85abf33--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1977.eml b/backend/demo/src/main/resources/turing/turing_award_1977.eml
index 51cafd472a852965fe53552e39ef0f12775f8b8c..74cac780b54e946985abb4855cb3cc8f7b649ec4 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1977.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1977.eml
@@ -1,39 +1,39 @@
-MIME-Version: 1.0
-From: "John W. Backus"
-Date: Mon, 17 Oct 1977 12:00:00 -0700
-Message-ID:
-Subject: Can Programming Be Liberated from the von Neumann Style? A Functional
- Style and Its Algebra of Programs
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7b5d9bdd8a36e804d85ade47
-
---047d7b5d9bdd8a36e804d85ade47
-Content-Type: text/plain; charset=UTF-8
-
-Conventional programming languages are growing ever more enormous, but not
-stronger. Inherent defects at the most basic level cause them to be both
-fat and weak: their primitive word-at-a-time style of programming inherited
-from their common ancestor--the von Neumann computer, their close coupling
-of semantics to state transitions, their division of programming into a
-world of expressions and a world of statements, their inability to
-effectively use powerful combining forms for building new programs from
-existing ones, and their lack of useful mathematical properties for
-reasoning about
-programs.
-
---047d7b5d9bdd8a36e804d85ade47
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-Conventional programming languages are growing ever m=
-ore enormous, but not stronger. Inherent defects at the most basic level ca=
-use them to be both fat and weak: their primitive word-at-a-time style of p=
-rogramming inherited from their common ancestor--the von Neumann computer, =
-their close coupling of semantics to state transitions, their division of p=
-rogramming into a world of expressions and a world of statements, their ina=
-bility to effectively use powerful combining forms for building new program=
-s from existing ones, and their lack of useful mathematical properties for =
-reasoning about=C2=A0
-programs.
-
---047d7b5d9bdd8a36e804d85ade47--
+MIME-Version: 1.0
+From: "John W. Backus"
+Date: Mon, 17 Oct 1977 12:00:00 -0700
+Message-ID:
+Subject: Can Programming Be Liberated from the von Neumann Style? A Functional
+ Style and Its Algebra of Programs
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7b5d9bdd8a36e804d85ade47
+
+--047d7b5d9bdd8a36e804d85ade47
+Content-Type: text/plain; charset=UTF-8
+
+Conventional programming languages are growing ever more enormous, but not
+stronger. Inherent defects at the most basic level cause them to be both
+fat and weak: their primitive word-at-a-time style of programming inherited
+from their common ancestor--the von Neumann computer, their close coupling
+of semantics to state transitions, their division of programming into a
+world of expressions and a world of statements, their inability to
+effectively use powerful combining forms for building new programs from
+existing ones, and their lack of useful mathematical properties for
+reasoning about
+programs.
+
+--047d7b5d9bdd8a36e804d85ade47
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+Conventional programming languages are growing ever m=
+ore enormous, but not stronger. Inherent defects at the most basic level ca=
+use them to be both fat and weak: their primitive word-at-a-time style of p=
+rogramming inherited from their common ancestor--the von Neumann computer, =
+their close coupling of semantics to state transitions, their division of p=
+rogramming into a world of expressions and a world of statements, their ina=
+bility to effectively use powerful combining forms for building new program=
+s from existing ones, and their lack of useful mathematical properties for =
+reasoning about=C2=A0
+programs.
+
+--047d7b5d9bdd8a36e804d85ade47--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1978.eml b/backend/demo/src/main/resources/turing/turing_award_1978.eml
index a1001324abb2da9cbbddb711443b368e3511d338..6dc52b24d7a4525c0f77b026aa6bcba436a9c367 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1978.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1978.eml
@@ -1,36 +1,36 @@
-MIME-Version: 1.0
-From: Robert Floyd
-Date: Mon, 04 Dec 1978 12:00:00 -0500
-Message-ID:
-Subject: The Paradigms of Programming
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=089e0118419206e64304d85af860
-
---089e0118419206e64304d85af860
-Content-Type: text/plain; charset=UTF-8
-
-Today I want to talk about the paradigms of programming, how they affect
-our success as designers of computer programs, how they should be taught,
-and how they should be embodied in our programming languages.
-A familiar example of a paradigm of programming is the technique of
-structured programming, which appears to be the dominant paradigm in most
-current treatments of programming methodology. Structured programming, as
-formulated by Dijkstra, Wirth, and Parnas, among others, consists of two
-phases.
-
---089e0118419206e64304d85af860
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-Today I want to talk about the paradigms of programmi=
-ng, how they affect our success as designers of computer programs, how they=
- should be taught, and how they should be embodied in our programming langu=
-ages.=C2=A0
-A familiar example of a paradigm of programming is the technique of st=
-ructured programming, which appears to be the dominant paradigm in most cur=
-rent treatments of programming methodology. Structured programming, as form=
-ulated by Dijkstra, Wirth, and Parnas, among others, consists of two phases=
-.=C2=A0
-
-
---089e0118419206e64304d85af860--
+MIME-Version: 1.0
+From: Robert Floyd
+Date: Mon, 04 Dec 1978 12:00:00 -0500
+Message-ID:
+Subject: The Paradigms of Programming
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=089e0118419206e64304d85af860
+
+--089e0118419206e64304d85af860
+Content-Type: text/plain; charset=UTF-8
+
+Today I want to talk about the paradigms of programming, how they affect
+our success as designers of computer programs, how they should be taught,
+and how they should be embodied in our programming languages.
+A familiar example of a paradigm of programming is the technique of
+structured programming, which appears to be the dominant paradigm in most
+current treatments of programming methodology. Structured programming, as
+formulated by Dijkstra, Wirth, and Parnas, among others, consists of two
+phases.
+
+--089e0118419206e64304d85af860
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+Today I want to talk about the paradigms of programmi=
+ng, how they affect our success as designers of computer programs, how they=
+ should be taught, and how they should be embodied in our programming langu=
+ages.=C2=A0
+A familiar example of a paradigm of programming is the technique of st=
+ructured programming, which appears to be the dominant paradigm in most cur=
+rent treatments of programming methodology. Structured programming, as form=
+ulated by Dijkstra, Wirth, and Parnas, among others, consists of two phases=
+.=C2=A0
+
+
+--089e0118419206e64304d85af860--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1979.eml b/backend/demo/src/main/resources/turing/turing_award_1979.eml
index 9322643ae4768117af4c6bc87847e43e38c2edfb..e61c5d3b105529c9e60c2a0d0ed7ecd7c21e600d 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1979.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1979.eml
@@ -1,33 +1,33 @@
-MIME-Version: 1.0
-From: "Kenneth E. Iverson"
-Date: Mon, 29 Oct 1979 12:00:00 -0500
-Message-ID:
-Subject: Notation as a Tool of Thought
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=20cf30549cad76254e04d85ae4df
-
---20cf30549cad76254e04d85ae4df
-Content-Type: text/plain; charset=UTF-8
-
-The importance of nomenclature, notation, and language as tools of thought
-has long been recognized. In chemistry and in botany, for example, the
-establishment of systems of nomenclature by Lavoisier and Linnaeus did much
-to stimulate and to channel later investigation. Concerning language,
-George Boole in his Laws off Thought asserted "That language is an
-instrument of human reason, and not merely a medium for the expression of
-thought, is a truth generally admitted."
-
---20cf30549cad76254e04d85ae4df
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-The importance of nomenclature, notation, and languag=
-e as tools of thought has long been recognized. In chemistry and in botany,=
- for example, the establishment of systems of nomenclature by Lavoisier and=
- Linnaeus did much to stimulate and to channel later investigation. Concern=
-ing language, George Boole in his Laws off Thought asserted "That lang=
-uage is an instrument of human reason, and not merely a medium for the expr=
-ession of thought, is a truth generally admitted."
-
-
---20cf30549cad76254e04d85ae4df--
+MIME-Version: 1.0
+From: "Kenneth E. Iverson"
+Date: Mon, 29 Oct 1979 12:00:00 -0500
+Message-ID:
+Subject: Notation as a Tool of Thought
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=20cf30549cad76254e04d85ae4df
+
+--20cf30549cad76254e04d85ae4df
+Content-Type: text/plain; charset=UTF-8
+
+The importance of nomenclature, notation, and language as tools of thought
+has long been recognized. In chemistry and in botany, for example, the
+establishment of systems of nomenclature by Lavoisier and Linnaeus did much
+to stimulate and to channel later investigation. Concerning language,
+George Boole in his Laws off Thought asserted "That language is an
+instrument of human reason, and not merely a medium for the expression of
+thought, is a truth generally admitted."
+
+--20cf30549cad76254e04d85ae4df
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+The importance of nomenclature, notation, and languag=
+e as tools of thought has long been recognized. In chemistry and in botany,=
+ for example, the establishment of systems of nomenclature by Lavoisier and=
+ Linnaeus did much to stimulate and to channel later investigation. Concern=
+ing language, George Boole in his Laws off Thought asserted "That lang=
+uage is an instrument of human reason, and not merely a medium for the expr=
+ession of thought, is a truth generally admitted."
+
+
+--20cf30549cad76254e04d85ae4df--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1981.eml b/backend/demo/src/main/resources/turing/turing_award_1981.eml
index 6dd8e51d42c0e122e342f8106ab5ca285b36deef..92fb6efa39d6f054fbbfde452435f2100757323c 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1981.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1981.eml
@@ -1,51 +1,51 @@
-MIME-Version: 1.0
-From: "Edgar F. Codd"
-Date: Wed, 11 Nov 1981 12:00:00 -0800
-Message-ID:
-Subject: Relational Database: A Practical Foundation for Productivity
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7bfd026c782f2404d85ab4b8
-
---047d7bfd026c782f2404d85ab4b8
-Content-Type: text/plain; charset=UTF-8
-
-It is well known that the growth in demands from end users for new
-applications is outstripping the capability of data processing departments
-to implement the corresponding application programs. There are two
-complementary approaches to attacking this problem (and both approaches are
-needed): one is to put end users into direct touch with the information
-stored in computers; the other is to increase the productivity of data
-processing professionals in the development of application programs. It is
-less well known that a single technology, relational database management,
-provides a practical foundation for both approaches. It is explained why
-this
-is so.
-While developing this productivity theme, it is noted that the time has
-come to draw a very sharp line between relational and non-relational
-database systems, so that the label "relational" will not be used in
-misleading ways.
-The key to drawing this line is something called a "relational processing
-capability."
-
---047d7bfd026c782f2404d85ab4b8
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-It is well known that the growth in demands from end =
-users for new applications is outstripping the capability of data processin=
-g departments to implement the corresponding application programs. There ar=
-e two complementary approaches to attacking this problem (and both approach=
-es are needed): one is to put end users into direct touch with the informat=
-ion stored in computers; the other is to increase the productivity of data =
-processing professionals in the development of application programs. It is =
-less well known that a single technology, relational database management, p=
-rovides a practical foundation for both approaches. It is explained why thi=
-s=C2=A0
-is so.=C2=A0While developing this productivity theme, =
-it is noted that the time has come to draw a very sharp line between relati=
-onal and non-relational database systems, so that the label "relationa=
-l" will not be used in misleading ways.=C2=A0
-The key to drawing this line is something called a "relational pr=
-ocessing capability."
-
---047d7bfd026c782f2404d85ab4b8--
+MIME-Version: 1.0
+From: "Edgar F. Codd"
+Date: Wed, 11 Nov 1981 12:00:00 -0800
+Message-ID:
+Subject: Relational Database: A Practical Foundation for Productivity
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7bfd026c782f2404d85ab4b8
+
+--047d7bfd026c782f2404d85ab4b8
+Content-Type: text/plain; charset=UTF-8
+
+It is well known that the growth in demands from end users for new
+applications is outstripping the capability of data processing departments
+to implement the corresponding application programs. There are two
+complementary approaches to attacking this problem (and both approaches are
+needed): one is to put end users into direct touch with the information
+stored in computers; the other is to increase the productivity of data
+processing professionals in the development of application programs. It is
+less well known that a single technology, relational database management,
+provides a practical foundation for both approaches. It is explained why
+this
+is so.
+While developing this productivity theme, it is noted that the time has
+come to draw a very sharp line between relational and non-relational
+database systems, so that the label "relational" will not be used in
+misleading ways.
+The key to drawing this line is something called a "relational processing
+capability."
+
+--047d7bfd026c782f2404d85ab4b8
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+It is well known that the growth in demands from end =
+users for new applications is outstripping the capability of data processin=
+g departments to implement the corresponding application programs. There ar=
+e two complementary approaches to attacking this problem (and both approach=
+es are needed): one is to put end users into direct touch with the informat=
+ion stored in computers; the other is to increase the productivity of data =
+processing professionals in the development of application programs. It is =
+less well known that a single technology, relational database management, p=
+rovides a practical foundation for both approaches. It is explained why thi=
+s=C2=A0
+is so.=C2=A0While developing this productivity theme, =
+it is noted that the time has come to draw a very sharp line between relati=
+onal and non-relational database systems, so that the label "relationa=
+l" will not be used in misleading ways.=C2=A0
+The key to drawing this line is something called a "relational pr=
+ocessing capability."
+
+--047d7bfd026c782f2404d85ab4b8--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1983.eml b/backend/demo/src/main/resources/turing/turing_award_1983.eml
index 798cdf521686ac780afc7e1d7ee26b596d0243d9..344f752c2f9ef06ca4ae711692ee8215498c9f72 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1983.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1983.eml
@@ -1,46 +1,46 @@
-MIME-Version: 1.0
-From: Dennis Ritchie
-Date: Mon, 24 Oct 1983 12:00:00 -0400
-Message-ID:
-Subject: Reflections on Software Research
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=bcaec54fbb2250035a04d85aabcd
-
---bcaec54fbb2250035a04d85aabcd
-Content-Type: text/plain; charset=UTF-8
-
-The UNIX operating system has suddenly become news, but it is not new. It
-began in 1969 when Ken Thompson discovered a little-used PDP-7 computer and
-set out to fashion a computing environment that he liked, His work soon
-attracted me; I joined in the enterprise, though most of the ideas, and
-most of the work for that matter, were his. Before long, others from our
-group in the research area of AT&T Bell Laboratories were using the system;
-Joe Ossanna, Doug Mcllroy, and
-Bob Morris were especially enthusiastic critics and contributors, tn 1971,
-we acquired a PDP-11, and by the end of that year we were supporting our
-first real users: three typists entering patent applications. In 1973, the
-system was rewritten in the C language, and in that year, too, it was first
-described publicly at the Operating Systems Principles conference; the
-resulting paper appeared in Communications of the ACM the next year.
-
---bcaec54fbb2250035a04d85aabcd
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-The UNIX operating system has suddenly become news, b=
-ut it is not new. It began in 1969 when Ken Thompson discovered a little-us=
-ed PDP-7 computer and set out to fashion a computing environment that he li=
-ked, His work soon attracted me; I joined in the enterprise, though most of=
- the ideas, and most of the work for that matter, were his. Before long, ot=
-hers from our group in the research area of AT&T Bell Laboratories were=
- using the system; Joe Ossanna, Doug Mcllroy, and=C2=A0
-Bob Morris were especially enthusiastic critics and contributors, tn 1=
-971, we acquired a PDP-11, and by the end of that year we were supporting o=
-ur first real users: three typists entering patent applications. In 1973, t=
-he system was rewritten in the C language, and in that year, too, it was fi=
-rst described publicly at the Operating Systems Principles conference; the =
-resulting paper appeared in Communications of the ACM the next year.=C2=A0<=
-/div>
-
-
---bcaec54fbb2250035a04d85aabcd--
+MIME-Version: 1.0
+From: Dennis Ritchie
+Date: Mon, 24 Oct 1983 12:00:00 -0400
+Message-ID:
+Subject: Reflections on Software Research
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=bcaec54fbb2250035a04d85aabcd
+
+--bcaec54fbb2250035a04d85aabcd
+Content-Type: text/plain; charset=UTF-8
+
+The UNIX operating system has suddenly become news, but it is not new. It
+began in 1969 when Ken Thompson discovered a little-used PDP-7 computer and
+set out to fashion a computing environment that he liked, His work soon
+attracted me; I joined in the enterprise, though most of the ideas, and
+most of the work for that matter, were his. Before long, others from our
+group in the research area of AT&T Bell Laboratories were using the system;
+Joe Ossanna, Doug Mcllroy, and
+Bob Morris were especially enthusiastic critics and contributors, tn 1971,
+we acquired a PDP-11, and by the end of that year we were supporting our
+first real users: three typists entering patent applications. In 1973, the
+system was rewritten in the C language, and in that year, too, it was first
+described publicly at the Operating Systems Principles conference; the
+resulting paper appeared in Communications of the ACM the next year.
+
+--bcaec54fbb2250035a04d85aabcd
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+The UNIX operating system has suddenly become news, b=
+ut it is not new. It began in 1969 when Ken Thompson discovered a little-us=
+ed PDP-7 computer and set out to fashion a computing environment that he li=
+ked, His work soon attracted me; I joined in the enterprise, though most of=
+ the ideas, and most of the work for that matter, were his. Before long, ot=
+hers from our group in the research area of AT&T Bell Laboratories were=
+ using the system; Joe Ossanna, Doug Mcllroy, and=C2=A0
+Bob Morris were especially enthusiastic critics and contributors, tn 1=
+971, we acquired a PDP-11, and by the end of that year we were supporting o=
+ur first real users: three typists entering patent applications. In 1973, t=
+he system was rewritten in the C language, and in that year, too, it was fi=
+rst described publicly at the Operating Systems Principles conference; the =
+resulting paper appeared in Communications of the ACM the next year.=C2=A0<=
+/div>
+
+
+--bcaec54fbb2250035a04d85aabcd--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1987.eml b/backend/demo/src/main/resources/turing/turing_award_1987.eml
index 945c454505aa7aee5d69e1ae04b802c592add223..2c4f82bbb043700de544742bc9d42351419c55c8 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1987.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1987.eml
@@ -1,42 +1,42 @@
-MIME-Version: 1.0
-From: John Cocke
-Date: Mon, 16 Feb 1987 12:00:00 -0600
-Message-ID:
-Subject: The Search for Performance in Scientific Processors
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7bfd079665fb2c04d85ad0bc
-
---047d7bfd079665fb2c04d85ad0bc
-Content-Type: text/plain; charset=UTF-8
-
-I am honored and grateful to have been selected to join the ranks of ACM
-Turing Award winners. I probably have spent too much of my life thinking
-about computers, but I do not regret it a bit. I was fortunate to enter the
-field of computing in its infancy and participate in its explosive growth.
-The rapid evolution of the underlying technologies in the past 30 years has
-not only provided an exciting environment, but has also presented a
-constant stream of intellectual challenges to those of us trying to harness
-this power and squeeze it to the last ounce. I hasten to say, especially to
-the
-younger members of the audience, there is no end in sight. As a matter of
-fact, I believe the next thirty years will be even more exciting and rich
-with challenges.
-
---047d7bfd079665fb2c04d85ad0bc
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-I am honored and grateful to have been selected to jo=
-in the ranks of ACM Turing Award winners. I probably have spent too much of=
- my life thinking about computers, but I do not regret it a bit. I was fort=
-unate to enter the field of computing in its infancy and participate in its=
- explosive growth. The rapid evolution of the underlying technologies in th=
-e past 30 years has not only provided an exciting environment, but has also=
- presented a constant stream of intellectual challenges to those of us tryi=
-ng to harness this power and squeeze it to the last ounce. I hasten to say,=
- especially to the=C2=A0
-younger members of the audience, there is no end in sight. As a matter=
- of fact, I believe the next thirty years will be even more exciting and ri=
-ch with challenges.=C2=A0
-
---047d7bfd079665fb2c04d85ad0bc--
+MIME-Version: 1.0
+From: John Cocke
+Date: Mon, 16 Feb 1987 12:00:00 -0600
+Message-ID:
+Subject: The Search for Performance in Scientific Processors
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7bfd079665fb2c04d85ad0bc
+
+--047d7bfd079665fb2c04d85ad0bc
+Content-Type: text/plain; charset=UTF-8
+
+I am honored and grateful to have been selected to join the ranks of ACM
+Turing Award winners. I probably have spent too much of my life thinking
+about computers, but I do not regret it a bit. I was fortunate to enter the
+field of computing in its infancy and participate in its explosive growth.
+The rapid evolution of the underlying technologies in the past 30 years has
+not only provided an exciting environment, but has also presented a
+constant stream of intellectual challenges to those of us trying to harness
+this power and squeeze it to the last ounce. I hasten to say, especially to
+the
+younger members of the audience, there is no end in sight. As a matter of
+fact, I believe the next thirty years will be even more exciting and rich
+with challenges.
+
+--047d7bfd079665fb2c04d85ad0bc
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+I am honored and grateful to have been selected to jo=
+in the ranks of ACM Turing Award winners. I probably have spent too much of=
+ my life thinking about computers, but I do not regret it a bit. I was fort=
+unate to enter the field of computing in its infancy and participate in its=
+ explosive growth. The rapid evolution of the underlying technologies in th=
+e past 30 years has not only provided an exciting environment, but has also=
+ presented a constant stream of intellectual challenges to those of us tryi=
+ng to harness this power and squeeze it to the last ounce. I hasten to say,=
+ especially to the=C2=A0
+younger members of the audience, there is no end in sight. As a matter=
+ of fact, I believe the next thirty years will be even more exciting and ri=
+ch with challenges.=C2=A0
+
+--047d7bfd079665fb2c04d85ad0bc--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1991.eml b/backend/demo/src/main/resources/turing/turing_award_1991.eml
index d068d60fa586b8da8b6d97ebd904ad2c4de3b844..57e31d48d91d389c332f2efe9b8973567edaf96f 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1991.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1991.eml
@@ -1,44 +1,44 @@
-MIME-Version: 1.0
-From: Robin Milner
-Date: Mon, 18 Nov 1991 12:00:00 -0700
-Message-ID:
-Subject: Elements of Interaction
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=047d7b86e6de64aecb04d85affff
-
---047d7b86e6de64aecb04d85affff
-Content-Type: text/plain; charset=UTF-8
-
-I am greatly honored to receive this award, bearing the name of Alan
-Turing. Perhaps Turing would be pleased that it should go to someone
-educated at his old college, King's College at Cambridge. While there in
-1956 I wrote my first computer program; it was on the EDSAC. Of course
-EDSAC made history. But I am ashamed to say it did not lure me into
-computing, and I ignored computers for four years. In 1960 I thought that
-computers might be more peaceful to handle than schoolchildren--I was then
-a teacher--so I applied for a job at Ferranti in London, at the time of
-Pegasus. I was asked at the interview whether I would like to devote my
-life to computers. This daunting notion had never crossed my mind. Well,
-here I am still, and I have had the lucky chance to grow alongside computer
-science.
-
---047d7b86e6de64aecb04d85affff
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-I am greatly honored to receive this award, bearing t=
-he name of Alan Turing. Perhaps Turing would be pleased that it should go t=
-o someone educated at his old college, King's College at Cambridge. Whi=
-le there in 1956 I wrote my first computer program; it was on the EDSAC. Of=
- course EDSAC made history. But I am ashamed to say it did not lure me into=
- computing, and I ignored computers for four years. In 1960 I thought that =
-computers might be more peaceful to handle than schoolchildren--I was then =
-a teacher--so I applied for a job at Ferranti in London, at the time of=C2=
-=A0
-Pegasus. I was asked at the interview whether I would like to devote m=
-y life to computers. This daunting notion had never crossed my mind. Well, =
-here I am still, and I have had the lucky chance to grow alongside computer=
- science.
-
-
---047d7b86e6de64aecb04d85affff--
+MIME-Version: 1.0
+From: Robin Milner
+Date: Mon, 18 Nov 1991 12:00:00 -0700
+Message-ID:
+Subject: Elements of Interaction
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=047d7b86e6de64aecb04d85affff
+
+--047d7b86e6de64aecb04d85affff
+Content-Type: text/plain; charset=UTF-8
+
+I am greatly honored to receive this award, bearing the name of Alan
+Turing. Perhaps Turing would be pleased that it should go to someone
+educated at his old college, King's College at Cambridge. While there in
+1956 I wrote my first computer program; it was on the EDSAC. Of course
+EDSAC made history. But I am ashamed to say it did not lure me into
+computing, and I ignored computers for four years. In 1960 I thought that
+computers might be more peaceful to handle than schoolchildren--I was then
+a teacher--so I applied for a job at Ferranti in London, at the time of
+Pegasus. I was asked at the interview whether I would like to devote my
+life to computers. This daunting notion had never crossed my mind. Well,
+here I am still, and I have had the lucky chance to grow alongside computer
+science.
+
+--047d7b86e6de64aecb04d85affff
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+I am greatly honored to receive this award, bearing t=
+he name of Alan Turing. Perhaps Turing would be pleased that it should go t=
+o someone educated at his old college, King's College at Cambridge. Whi=
+le there in 1956 I wrote my first computer program; it was on the EDSAC. Of=
+ course EDSAC made history. But I am ashamed to say it did not lure me into=
+ computing, and I ignored computers for four years. In 1960 I thought that =
+computers might be more peaceful to handle than schoolchildren--I was then =
+a teacher--so I applied for a job at Ferranti in London, at the time of=C2=
+=A0
+Pegasus. I was asked at the interview whether I would like to devote m=
+y life to computers. This daunting notion had never crossed my mind. Well, =
+here I am still, and I have had the lucky chance to grow alongside computer=
+ science.
+
+
+--047d7b86e6de64aecb04d85affff--
diff --git a/backend/demo/src/main/resources/turing/turing_award_1996.eml b/backend/demo/src/main/resources/turing/turing_award_1996.eml
index fb04a52d9f82a796cc5c3c3db5214f06c99a0ade..2f8bb13a68de4f135c2984620e14b931353bb503 100644
--- a/backend/demo/src/main/resources/turing/turing_award_1996.eml
+++ b/backend/demo/src/main/resources/turing/turing_award_1996.eml
@@ -1,28 +1,28 @@
-MIME-Version: 1.0
-From: Amir Pnueli
-Date: Thu, 15 Feb 1996 12:00:00 -0500
-Message-ID:
-Subject: Verification Engineering: A Future Profession
-To: Alan Turing
-Content-Type: multipart/alternative; boundary=bcaec54fbb222acf6704d85aa523
-
---bcaec54fbb222acf6704d85aa523
-Content-Type: text/plain; charset=UTF-8
-
-It is time that formal verification (of both software and hardware systems)
-be demoted from an art practiced by the enlightened few to an activity
-routinely and mundanely performed by a cadre of Verification Engineers (a
-new profession), as a standard part of the system development process.
-
---bcaec54fbb222acf6704d85aa523
-Content-Type: text/html; charset=UTF-8
-Content-Transfer-Encoding: quoted-printable
-
-It is time that formal verification (of both software=
- and hardware systems) be demoted from an art practiced by the enlightened =
-few to an activity routinely and mundanely performed by a cadre of Verifica=
-tion Engineers (a new profession), as a standard part of the system develop=
-ment process.
-
-
---bcaec54fbb222acf6704d85aa523--
+MIME-Version: 1.0
+From: Amir Pnueli
+Date: Thu, 15 Feb 1996 12:00:00 -0500
+Message-ID:
+Subject: Verification Engineering: A Future Profession
+To: Alan Turing
+Content-Type: multipart/alternative; boundary=bcaec54fbb222acf6704d85aa523
+
+--bcaec54fbb222acf6704d85aa523
+Content-Type: text/plain; charset=UTF-8
+
+It is time that formal verification (of both software and hardware systems)
+be demoted from an art practiced by the enlightened few to an activity
+routinely and mundanely performed by a cadre of Verification Engineers (a
+new profession), as a standard part of the system development process.
+
+--bcaec54fbb222acf6704d85aa523
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+
+It is time that formal verification (of both software=
+ and hardware systems) be demoted from an art practiced by the enlightened =
+few to an activity routinely and mundanely performed by a cadre of Verifica=
+tion Engineers (a new profession), as a standard part of the system develop=
+ment process.
+
+
+--bcaec54fbb222acf6704d85aa523--
diff --git a/backend/jmap/src/test/resources/jmap_responses/blob/email/email_1.eml b/backend/jmap/src/test/resources/jmap_responses/blob/email/email_1.eml
index 2847d4f1992c73dc1753c595fa68df0d90a2f74a..803a3e1dd258588396cea6740c5a108246998cec 100644
--- a/backend/jmap/src/test/resources/jmap_responses/blob/email/email_1.eml
+++ b/backend/jmap/src/test/resources/jmap_responses/blob/email/email_1.eml
@@ -1,14 +1,14 @@
-From: alice@domain.example
-To: bob@domain.example
-Message-ID:
-Date: Mon, 10 Feb 2020 10:20:30 +0100
-Subject: Hello there
-Content-Type: text/plain; charset=UTF-8
-Mime-Version: 1.0
-
-Hi Bob,
-
-this is a message from me to you.
-
-Cheers,
-Alice
+From: alice@domain.example
+To: bob@domain.example
+Message-ID:
+Date: Mon, 10 Feb 2020 10:20:30 +0100
+Subject: Hello there
+Content-Type: text/plain; charset=UTF-8
+Mime-Version: 1.0
+
+Hi Bob,
+
+this is a message from me to you.
+
+Cheers,
+Alice
diff --git a/backend/jmap/src/test/resources/jmap_responses/blob/email/email_2.eml b/backend/jmap/src/test/resources/jmap_responses/blob/email/email_2.eml
index 57faecf5200b72e77e1001b9079cb7fd298948b9..fcff100daa74196be1f737e9e3d128e2d80e2f11 100644
--- a/backend/jmap/src/test/resources/jmap_responses/blob/email/email_2.eml
+++ b/backend/jmap/src/test/resources/jmap_responses/blob/email/email_2.eml
@@ -1,16 +1,16 @@
-From: Bob
-To: alice@domain.example
-Message-ID:
-In-Reply-To:
-References:
-Date: Mon, 10 Feb 2020 10:20:30 +0100
-Subject: Re: Hello there
-Content-Type: text/plain; charset=UTF-8
-Mime-Version: 1.0
-
-Hi Alice,
-
-I've received your message.
-
-Best,
-Bob
+From: Bob
+To: alice@domain.example
+Message-ID:
+In-Reply-To:
+References:
+Date: Mon, 10 Feb 2020 10:20:30 +0100
+Subject: Re: Hello there
+Content-Type: text/plain; charset=UTF-8
+Mime-Version: 1.0
+
+Hi Alice,
+
+I've received your message.
+
+Best,
+Bob
diff --git a/backend/jmap/src/test/resources/jmap_responses/blob/email/email_3.eml b/backend/jmap/src/test/resources/jmap_responses/blob/email/email_3.eml
index 46cd0c319c08ca73d0b10cb8571841e8eda75551..6fa9b27fdd664aaf313bab67e1bfb4d8db8e9473 100644
--- a/backend/jmap/src/test/resources/jmap_responses/blob/email/email_3.eml
+++ b/backend/jmap/src/test/resources/jmap_responses/blob/email/email_3.eml
@@ -1,9 +1,9 @@
-From: alice@domain.example
-To: alice@domain.example
-Message-ID:
-Date: Mon, 10 Feb 2020 12:20:30 +0100
-Subject: Dummy
-Content-Type: text/plain; charset=UTF-8
-Mime-Version: 1.0
-
--
+From: alice@domain.example
+To: alice@domain.example
+Message-ID:
+Date: Mon, 10 Feb 2020 12:20:30 +0100
+Subject: Dummy
+Content-Type: text/plain; charset=UTF-8
+Mime-Version: 1.0
+
+-
diff --git a/build-plugin/src/main/kotlin/AndroidExtension.kt b/build-plugin/src/main/kotlin/AndroidExtension.kt
index 5db5fdcb336d5f9d8ee75c077cbfdfa48808ad7d..6fa5d3f64945c86b8318382955f84d9862ba1f05 100644
--- a/build-plugin/src/main/kotlin/AndroidExtension.kt
+++ b/build-plugin/src/main/kotlin/AndroidExtension.kt
@@ -2,7 +2,7 @@ import com.android.build.api.dsl.CommonExtension
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.artifacts.dsl.DependencyHandler
-internal fun CommonExtension<*, *, *, *>.configureSharedConfig() {
+internal fun CommonExtension<*, *, *, *, *>.configureSharedConfig() {
compileSdk = ThunderbirdProjectConfig.androidSdkCompile
defaultConfig {
@@ -29,7 +29,7 @@ internal fun CommonExtension<*, *, *, *>.configureSharedConfig() {
}
}
-internal fun CommonExtension<*, *, *, *>.configureSharedComposeConfig(
+internal fun CommonExtension<*, *, *, *, *>.configureSharedComposeConfig(
libs: LibrariesForLibs,
) {
buildFeatures {
@@ -41,7 +41,7 @@ internal fun CommonExtension<*, *, *, *>.configureSharedComposeConfig(
}
lint {
- warningsAsErrors = true
+ warningsAsErrors = false
abortOnError = true
}
diff --git a/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt b/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt
index c16a74dd5c8950cc833f4accab7ad593a86e40d9..4d43299295b818b4d1516add40a6ab1fdb1923a2 100644
--- a/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt
+++ b/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt
@@ -4,7 +4,7 @@ object ThunderbirdProjectConfig {
val javaCompatibilityVersion = JavaVersion.VERSION_11
- const val androidSdkMin = 21
+ const val androidSdkMin = 26
const val androidSdkTarget = 31
- const val androidSdkCompile = 33
+ const val androidSdkCompile = 34
}
diff --git a/build-plugin/src/main/kotlin/thunderbird.quality.spotless.gradle.kts b/build-plugin/src/main/kotlin/thunderbird.quality.spotless.gradle.kts
index d799629a7c20d21bc5ad767e9520c83215a13f60..e59057cb4befe88c012c0fcb3faee2b6d9017186 100644
--- a/build-plugin/src/main/kotlin/thunderbird.quality.spotless.gradle.kts
+++ b/build-plugin/src/main/kotlin/thunderbird.quality.spotless.gradle.kts
@@ -7,6 +7,7 @@ plugins {
configure {
kotlin {
ktlint(libs.versions.ktlint.get())
+ .userData(mapOf("android" to "true"))
target("**/*.kt")
targetExclude("**/build/", "**/resources/", "plugins/openpgp-api-lib/")
}
diff --git a/core/common/src/main/kotlin/app/k9mail/core/common/net/HostNameUtils.kt b/core/common/src/main/kotlin/app/k9mail/core/common/net/HostNameUtils.kt
index 91e1f2ba3ac67a38af6d3fa47777e8d049167cfb..d8e7729aca1d9cea25dbcbcf58345c9f5e067a98 100644
--- a/core/common/src/main/kotlin/app/k9mail/core/common/net/HostNameUtils.kt
+++ b/core/common/src/main/kotlin/app/k9mail/core/common/net/HostNameUtils.kt
@@ -24,7 +24,7 @@ object HostNameUtils {
address in dotted-decimal ("#.#.#.#") form. The host SHOULD check
the string syntactically for a dotted-decimal number before
looking it up in the Domain Name System.
- */
+ */
return isLegalIPAddress(hostName) ?: isLegalHostName(hostName)
}
@@ -204,7 +204,7 @@ object HostNameUtils {
Since a complete domain name ends with the root label, this leads to
a printed form which ends in a dot.
- */
+ */
return hostName.takeIf { hostName.length <= 255 && HOST_PATTERN.matches(hostName) }
}
diff --git a/core/featureflags/src/main/kotlin/app/k9mail/core/featureflag/FeatureFlagKey.kt b/core/featureflags/src/main/kotlin/app/k9mail/core/featureflag/FeatureFlagKey.kt
index d7901d229a8dc91ffb1b35caa82333ceb5930d83..4e54b402d4bcff7ddce7430a2e3ed1fc012d100e 100644
--- a/core/featureflags/src/main/kotlin/app/k9mail/core/featureflag/FeatureFlagKey.kt
+++ b/core/featureflags/src/main/kotlin/app/k9mail/core/featureflag/FeatureFlagKey.kt
@@ -2,3 +2,5 @@ package app.k9mail.core.featureflag
@JvmInline
value class FeatureFlagKey(val key: String)
+
+fun String.toFeatureFlagKey(): FeatureFlagKey = FeatureFlagKey(this)
diff --git a/core/ui/compose/common/build.gradle.kts b/core/ui/compose/common/build.gradle.kts
index d1b76a4dd5ee9c36c768583c00f41e3ad3052c59..88b78e4cd153ea027ea19c542ebf2535997b6168 100644
--- a/core/ui/compose/common/build.gradle.kts
+++ b/core/ui/compose/common/build.gradle.kts
@@ -8,5 +8,7 @@ android {
}
dependencies {
+ implementation(libs.androidx.compose.activity)
+
testImplementation(projects.core.ui.compose.testing)
}
diff --git a/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/mvi/UnidirectionalViewModel.kt b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/mvi/UnidirectionalViewModel.kt
index f79840deff59f775f23b736bd3e21cf4804cdbb7..fb8f9685adff547be960c06d6c9817d6b1eaf9f5 100644
--- a/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/mvi/UnidirectionalViewModel.kt
+++ b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/mvi/UnidirectionalViewModel.kt
@@ -102,3 +102,45 @@ inline fun UnidirectionalViewModel,
+ * onNavigateNext: () -> Unit,
+ * onNavigateBack: () -> Unit,
+ * ) {
+ * val (state, dispatch) = viewModel.observeWithoutEffect()
+ *
+ * MyContent(
+ * onNextClick = {
+ * dispatch(MyEvent.OnNext)
+ * },
+ * onBackClick = {
+ * dispatch(MyEvent.OnBack)
+ * },
+ * state = state.value,
+ * )
+ * }
+ * ```
+ *
+ * @param STATE The type that represents the state of the ViewModel.
+ * @param EVENT The type that represents user actions that can occur and should be handled by the ViewModel.
+ *
+ * @return A [StateDispatch] containing the state and a dispatch function.
+ */
+@Suppress("MaxLineLength")
+@Composable
+inline fun UnidirectionalViewModel.observeWithoutEffect(): StateDispatch {
+ val collectedState = state.collectAsStateWithLifecycle()
+ val dispatch: (EVENT) -> Unit = { event(it) }
+
+ return StateDispatch(
+ state = collectedState,
+ dispatch = dispatch,
+ )
+}
diff --git a/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/navigation/NavBackStackEntryExtension.kt b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/navigation/NavBackStackEntryExtension.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c442548623d082169d7447c623d49a03d9102914
--- /dev/null
+++ b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/navigation/NavBackStackEntryExtension.kt
@@ -0,0 +1,7 @@
+package app.k9mail.core.ui.compose.common.navigation
+
+import androidx.navigation.NavBackStackEntry
+
+fun NavBackStackEntry.getStringArgument(key: String): String {
+ return arguments?.getString(key) ?: error("Missing argument: $key")
+}
diff --git a/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/navigation/NavigationExtensions.kt b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/navigation/NavigationExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bce88f0686120067dcce03e7c1b271d263f61de9
--- /dev/null
+++ b/core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/navigation/NavigationExtensions.kt
@@ -0,0 +1,30 @@
+package app.k9mail.core.ui.compose.common.navigation
+
+import android.net.Uri
+import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.runtime.Composable
+import androidx.core.net.toUri
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import androidx.navigation.navDeepLink
+
+fun NavGraphBuilder.deepLinkComposable(
+ route: String,
+ arguments: List = emptyList(),
+ content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit,
+) {
+ composable(
+ route = route,
+ arguments = arguments,
+ deepLinks = listOf(
+ navDeepLink { uriPattern = route.toDeepLink() },
+ ),
+ content = content,
+ )
+}
+
+fun String.toDeepLink(): String = "app://$this"
+
+fun String.toDeepLinkUri(): Uri = toDeepLink().toUri()
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt
index fcf3cb0121ae417ad93dc5c80ca238e350194a0d..f34727a973e17210215245b18bed00208340f1e6 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt
@@ -36,7 +36,7 @@ private const val ASTERISK = "*"
@Preview(showBackground = true)
@Composable
-fun TextFieldLabelPreview() {
+internal fun TextFieldLabelPreview() {
PreviewWithThemes {
TextFieldLabel(
label = "Label",
@@ -47,7 +47,7 @@ fun TextFieldLabelPreview() {
@Preview(showBackground = true)
@Composable
-fun TextFieldLabelRequiredPreview() {
+internal fun TextFieldLabelRequiredPreview() {
PreviewWithThemes {
TextFieldLabel(
label = "Label",
@@ -58,7 +58,7 @@ fun TextFieldLabelRequiredPreview() {
@Preview(showBackground = true)
@Composable
-fun TextFieldLabelRequiredEmptyLabelPreview() {
+internal fun TextFieldLabelRequiredEmptyLabelPreview() {
PreviewWithThemes {
TextFieldLabel(
label = "",
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3985665b85b87dee8a2e6c80321d6ce6ffef0938
--- /dev/null
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt
@@ -0,0 +1,86 @@
+package app.k9mail.core.ui.compose.designsystem.molecule
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextSubtitle1
+import app.k9mail.core.ui.compose.theme.PreviewWithThemes
+
+@OptIn(ExperimentalAnimationApi::class)
+@Composable
+fun ContentLoadingErrorView(
+ state: ContentLoadingErrorState,
+ loading: @Composable () -> Unit,
+ error: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ contentAlignment: Alignment = Alignment.Center,
+ content: @Composable () -> Unit,
+) {
+ Box(
+ modifier = modifier,
+ contentAlignment = contentAlignment,
+ ) {
+ AnimatedContent(
+ targetState = state,
+ label = "ContentLoadingErrorView",
+ ) { targetState ->
+ when (targetState) {
+ ContentLoadingErrorState.Loading -> loading()
+ ContentLoadingErrorState.Content -> content()
+ ContentLoadingErrorState.Error -> error()
+ }
+ }
+ }
+}
+
+enum class ContentLoadingErrorState {
+ Loading, Content, Error
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun ContentLoadingErrorViewPreview() {
+ PreviewWithThemes {
+ val state = remember {
+ mutableStateOf(ContentLoadingErrorState.Loading)
+ }
+
+ ContentLoadingErrorView(
+ state = state.value,
+ modifier = Modifier
+ .clickable {
+ when (state.value) {
+ ContentLoadingErrorState.Loading -> {
+ state.value = ContentLoadingErrorState.Content
+ }
+
+ ContentLoadingErrorState.Content -> {
+ state.value = ContentLoadingErrorState.Error
+ }
+
+ ContentLoadingErrorState.Error -> {
+ state.value = ContentLoadingErrorState.Loading
+ }
+ }
+ }
+ .fillMaxSize(),
+ error = {
+ TextSubtitle1(text = "Error")
+ },
+ loading = {
+ TextSubtitle1(text = "Loading...")
+ },
+ content = {
+ TextSubtitle1(text = "Content")
+ },
+ )
+ }
+}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt
index 54f7d38c31be6f9c1aad5bfa204d1afc9945b043..d60281fcef9bbe2330aa98851a7406ce4130bac2 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt
@@ -1,6 +1,7 @@
package app.k9mail.core.ui.compose.designsystem.molecule
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -26,49 +27,56 @@ fun ErrorView(
modifier: Modifier = Modifier,
message: String? = null,
onRetry: () -> Unit = { },
+ contentAlignment: Alignment = Alignment.Center,
) {
- Column(
+ Box(
modifier = Modifier
.fillMaxWidth()
- .padding(
- vertical = MainTheme.spacings.default,
- horizontal = MainTheme.spacings.double,
- )
.then(modifier),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
+ contentAlignment = contentAlignment,
) {
- Icon(
- imageVector = Icons.Filled.error,
- contentDescription = null,
- tint = MainTheme.colors.error,
- modifier = Modifier.padding(top = MainTheme.spacings.default),
- )
- TextSubtitle1(
- text = title,
- modifier = Modifier.padding(bottom = MainTheme.spacings.default),
- )
- if (message != null) {
- TextBody2(
- text = message,
- modifier = Modifier
- .fillMaxWidth(),
- )
- }
- Row(
+ Column(
modifier = Modifier
- .fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.End,
- ) {
- ButtonText(
- text = stringResource(id = R.string.designsystem_molecule_error_view_button_retry),
- onClick = onRetry,
- contentPadding = buttonContentPadding(
- start = MainTheme.spacings.double,
- end = MainTheme.spacings.double,
+ .fillMaxWidth()
+ .padding(
+ vertical = MainTheme.spacings.default,
+ horizontal = MainTheme.spacings.double,
),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
+ ) {
+ Icon(
+ imageVector = Icons.Filled.error,
+ contentDescription = null,
+ tint = MainTheme.colors.error,
+ modifier = Modifier.padding(top = MainTheme.spacings.default),
)
+ TextSubtitle1(
+ text = title,
+ modifier = Modifier.padding(bottom = MainTheme.spacings.default),
+ )
+ if (message != null) {
+ TextBody2(
+ text = message,
+ modifier = Modifier
+ .fillMaxWidth(),
+ )
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.End,
+ ) {
+ ButtonText(
+ text = stringResource(id = R.string.designsystem_molecule_error_view_button_retry),
+ onClick = onRetry,
+ contentPadding = buttonContentPadding(
+ start = MainTheme.spacings.double,
+ end = MainTheme.spacings.double,
+ ),
+ )
+ }
}
}
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt
index cb799625b5410837917662f940ff4391b0771ddf..3266398109ac004df73af4c686f75d81299aa68c 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt
@@ -1,5 +1,7 @@
package app.k9mail.core.ui.compose.designsystem.molecule
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -18,22 +20,31 @@ import app.k9mail.core.ui.compose.theme.PreviewWithThemes
fun LoadingView(
modifier: Modifier = Modifier,
message: String? = null,
+ contentAlignment: Alignment = Alignment.Center,
) {
- Column(
+ Box(
modifier = Modifier
.fillMaxWidth()
- .padding(MainTheme.spacings.default)
.then(modifier),
- horizontalAlignment = Alignment.CenterHorizontally,
+ contentAlignment = contentAlignment,
) {
- if (message != null) {
- TextSubtitle1(text = message)
- }
- Row(
- modifier = Modifier.height(MainTheme.sizes.larger),
- verticalAlignment = Alignment.CenterVertically,
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(MainTheme.spacings.default)
+ .then(modifier),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
) {
- CircularProgressIndicator()
+ if (message != null) {
+ TextSubtitle1(text = message)
+ }
+ Row(
+ modifier = Modifier.height(MainTheme.sizes.larger),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ CircularProgressIndicator()
+ }
}
}
}
diff --git a/docs/activity_diagram.graphml b/docs/activity_diagram.graphml
index a2f1a421690383f61d25082b8c90af966c42686f..05a291a4f8264e98e8ee0dfb1a5fa459a5fa7f30 100644
--- a/docs/activity_diagram.graphml
+++ b/docs/activity_diagram.graphml
@@ -1,936 +1,936 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Accounts
-
-
-
-
-
-
-
-
-
-
- FolderList
-
-
-
-
-
-
-
-
-
-
- MessageList
-
-
-
-
-
-
-
-
-
-
- MessageView
-
-
-
-
-
-
-
-
-
-
- AccountSettings
-
-
-
-
-
-
-
-
-
-
- AccountSetupIncoming
-
-
-
-
-
-
-
-
-
-
- AccountSetupOutgoing
-
-
-
-
-
-
-
-
-
-
- Prefs
-
-
-
-
-
-
-
-
-
-
- FontSizeSettings
-
-
-
-
-
-
-
-
-
-
- AccountSetupOptions
-
-
-
-
-
-
-
-
-
-
- AccountSetupNames
-
-
-
-
-
-
-
-
-
-
- AccountSetupComposition
-
-
-
-
-
-
-
-
-
-
- AccountSetupCheckSettings
-
-
-
-
-
-
-
-
-
-
- AccountSetupBasics
-
-
-
-
-
-
-
-
-
-
- AccountSetupAccountType
-
-
-
-
-
-
-
-
-
-
- FolderSettings
-
-
-
-
-
-
-
-
-
-
- ChooseAccount
-
-
-
-
-
-
-
-
-
-
- ChooseFolder
-
-
-
-
-
-
-
-
-
-
- ChooseIdentity
-
-
-
-
-
-
-
-
-
-
- EditIdentity
-
-
-
-
-
-
-
-
-
-
- LauncherShortcuts
-
-
-
-
-
-
-
-
-
-
- ManageIdentities
-
-
-
-
-
-
-
-
-
-
- MessageCompose
-
-
-
-
-
-
-
-
-
-
- Other Apps / OS
-
-
-
-
-
-
-
-
-
-
- Search
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Accounts
+
+
+
+
+
+
+
+
+
+
+ FolderList
+
+
+
+
+
+
+
+
+
+
+ MessageList
+
+
+
+
+
+
+
+
+
+
+ MessageView
+
+
+
+
+
+
+
+
+
+
+ AccountSettings
+
+
+
+
+
+
+
+
+
+
+ AccountSetupIncoming
+
+
+
+
+
+
+
+
+
+
+ AccountSetupOutgoing
+
+
+
+
+
+
+
+
+
+
+ Prefs
+
+
+
+
+
+
+
+
+
+
+ FontSizeSettings
+
+
+
+
+
+
+
+
+
+
+ AccountSetupOptions
+
+
+
+
+
+
+
+
+
+
+ AccountSetupNames
+
+
+
+
+
+
+
+
+
+
+ AccountSetupComposition
+
+
+
+
+
+
+
+
+
+
+ AccountSetupCheckSettings
+
+
+
+
+
+
+
+
+
+
+ AccountSetupBasics
+
+
+
+
+
+
+
+
+
+
+ AccountSetupAccountType
+
+
+
+
+
+
+
+
+
+
+ FolderSettings
+
+
+
+
+
+
+
+
+
+
+ ChooseAccount
+
+
+
+
+
+
+
+
+
+
+ ChooseFolder
+
+
+
+
+
+
+
+
+
+
+ ChooseIdentity
+
+
+
+
+
+
+
+
+
+
+ EditIdentity
+
+
+
+
+
+
+
+
+
+
+ LauncherShortcuts
+
+
+
+
+
+
+
+
+
+
+ ManageIdentities
+
+
+
+
+
+
+
+
+
+
+ MessageCompose
+
+
+
+
+
+
+
+
+
+
+ Other Apps / OS
+
+
+
+
+
+
+
+
+
+
+ Search
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fastlane/metadata/android/en-US/changelogs/37011.txt b/fastlane/metadata/android/en-US/changelogs/37011.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d9d64e4ebdc702696811c52362742be716eab07f
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/37011.txt
@@ -0,0 +1,8 @@
+- Simplified the app icon so it can be a vector drawable
+- Improved screen reader experience in various places
+- Improved logging on failing settings import
+- Improved display of some HTML messages
+- Changed background color in message view and compose screens when using dark theme
+- Fixed OAuth 2.0 with Yahoo and AOL
+- Fixed display issues when rendering a message/rfc822 inline part
+- Updated translations
diff --git a/feature/account/common/build.gradle.kts b/feature/account/common/build.gradle.kts
index 743c8e4e00c2e7e6e299fd3c8b8fed06426ce224..aa32047961973077506685ae710e33513f0a1624 100644
--- a/feature/account/common/build.gradle.kts
+++ b/feature/account/common/build.gradle.kts
@@ -11,5 +11,7 @@ dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.common)
+ implementation(projects.mail.common)
+
testImplementation(projects.core.ui.compose.testing)
}
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/AccountCommonExternalContract.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/AccountCommonExternalContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3d8fe3c18f9ff509ebdbdae74392ceeec4fa51b3
--- /dev/null
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/AccountCommonExternalContract.kt
@@ -0,0 +1,10 @@
+package app.k9mail.feature.account.common
+
+import app.k9mail.feature.account.common.domain.entity.AccountState
+
+interface AccountCommonExternalContract {
+
+ fun interface AccountStateLoader {
+ suspend fun loadAccountState(accountUuid: String): AccountState?
+ }
+}
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/AccountCommonModule.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/AccountCommonModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b186198f8944a42d6f9659aadd490cecea986621
--- /dev/null
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/AccountCommonModule.kt
@@ -0,0 +1,17 @@
+package app.k9mail.feature.account.common
+
+import app.k9mail.core.common.coreCommonModule
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import com.fsck.k9.mail.oauth.AuthStateStorage
+import org.koin.core.module.Module
+import org.koin.dsl.binds
+import org.koin.dsl.module
+
+val featureAccountCommonModule: Module = module {
+ includes(coreCommonModule)
+
+ single {
+ InMemoryAccountStateRepository()
+ }.binds(arrayOf(AccountDomainContract.AccountStateRepository::class, AuthStateStorage::class))
+}
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a23b845da202888212574753bd4abdb1e1a249b4
--- /dev/null
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt
@@ -0,0 +1,57 @@
+package app.k9mail.feature.account.common.data
+
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import com.fsck.k9.mail.ServerSettings
+import com.fsck.k9.mail.oauth.AuthStateStorage
+
+class InMemoryAccountStateRepository(
+ private var state: AccountState = AccountState(),
+) : AccountDomainContract.AccountStateRepository, AuthStateStorage {
+
+ override fun getState(): AccountState {
+ return state
+ }
+
+ override fun setState(accountState: AccountState) {
+ state = accountState
+ }
+
+ override fun setEmailAddress(emailAddress: String) {
+ state = state.copy(emailAddress = emailAddress)
+ }
+
+ override fun setIncomingServerSettings(serverSettings: ServerSettings) {
+ state = state.copy(incomingServerSettings = serverSettings)
+ }
+
+ override fun setOutgoingServerSettings(serverSettings: ServerSettings) {
+ state = state.copy(outgoingServerSettings = serverSettings)
+ }
+
+ override fun setAuthorizationState(authorizationState: AuthorizationState) {
+ state = state.copy(authorizationState = authorizationState)
+ }
+
+ override fun setOptions(options: AccountOptions) {
+ state = state.copy(options = options)
+ }
+
+ override fun clear() {
+ state = AccountState()
+ }
+
+ override fun getAuthorizationState(): String? {
+ return state.authorizationState?.state
+ }
+
+ override fun updateAuthorizationState(authorizationState: String?) {
+ state = state.copy(authorizationState = AuthorizationState(authorizationState))
+ }
+
+ override fun getEmail(): String? {
+ return state.emailAddress
+ }
+}
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4d325904b28da90701b65cc492b102a832766376
--- /dev/null
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt
@@ -0,0 +1,27 @@
+package app.k9mail.feature.account.common.domain
+
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import com.fsck.k9.mail.ServerSettings
+
+interface AccountDomainContract {
+
+ interface AccountStateRepository {
+ fun getState(): AccountState
+
+ fun setState(accountState: AccountState)
+
+ fun setEmailAddress(emailAddress: String)
+
+ fun setIncomingServerSettings(serverSettings: ServerSettings)
+
+ fun setOutgoingServerSettings(serverSettings: ServerSettings)
+
+ fun setAuthorizationState(authorizationState: AuthorizationState)
+
+ fun setOptions(options: AccountOptions)
+
+ fun clear()
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/Account.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/Account.kt
similarity index 76%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/Account.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/Account.kt
index 0edc11e48034bcc9d26b84a6609c7e04689d664b..9fed2db7b78cf68cbea019c302f8a65aaf7b9cfc 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/Account.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/Account.kt
@@ -1,8 +1,9 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import com.fsck.k9.mail.ServerSettings
data class Account(
+ val uuid: String,
val emailAddress: String,
val incomingServerSettings: ServerSettings,
val outgoingServerSettings: ServerSettings,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AccountOptions.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountOptions.kt
similarity index 80%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AccountOptions.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountOptions.kt
index 403af527ac341c63cf540db266b399730514ce7e..21d8ffb0e6be5e5fef69d1f76566de1bc86af2cb 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AccountOptions.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountOptions.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
data class AccountOptions(
val accountName: String,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AccountSetupState.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt
similarity index 64%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AccountSetupState.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt
index db9cd1431e6bdab2983cb2ca3f6342d4cac8ef95..ae10bd2f3ac83231cc673022f92a3c1307672930 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AccountSetupState.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt
@@ -1,9 +1,9 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
import com.fsck.k9.mail.ServerSettings
-data class AccountSetupState(
+data class AccountState(
+ val uuid: String? = null,
val emailAddress: String? = null,
val incomingServerSettings: ServerSettings? = null,
val outgoingServerSettings: ServerSettings? = null,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AuthenticationType.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt
similarity index 96%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AuthenticationType.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt
index 77591f7956f79aefb4c9632a9366de0912a7b22b..c6965b23561d3127f12478adbae5f67a88513602 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AuthenticationType.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import com.fsck.k9.mail.AuthType
import kotlinx.collections.immutable.toImmutableList
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationState.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt
similarity index 53%
rename from feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationState.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt
index 16fb9e649ffd1b05159fa0d78c2755c891cc2d72..5d4e46fef520723bd01f852fe86e3e83a6bd2f5b 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationState.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.oauth.domain.entity
+package app.k9mail.feature.account.common.domain.entity
data class AuthorizationState(
val state: String? = null,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/ConnectionSecurity.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/ConnectionSecurity.kt
similarity index 60%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/ConnectionSecurity.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/ConnectionSecurity.kt
index beb5f18836d60a45c32aab4b429b3f91b06a6e11..0696af5afc221363e31729c484c0bfc4969d4733 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/ConnectionSecurity.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/ConnectionSecurity.kt
@@ -1,8 +1,8 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity.None
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity.StartTLS
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity.TLS
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity.None
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity.StartTLS
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity.TLS
import kotlinx.collections.immutable.toImmutableList
enum class ConnectionSecurity {
@@ -17,7 +17,7 @@ enum class ConnectionSecurity {
}
}
-internal fun ConnectionSecurity.toMailConnectionSecurity(): MailConnectionSecurity {
+fun ConnectionSecurity.toMailConnectionSecurity(): MailConnectionSecurity {
return when (this) {
None -> MailConnectionSecurity.NONE
StartTLS -> MailConnectionSecurity.STARTTLS_REQUIRED
@@ -25,7 +25,7 @@ internal fun ConnectionSecurity.toMailConnectionSecurity(): MailConnectionSecuri
}
}
-internal fun MailConnectionSecurity.toConnectionSecurity(): ConnectionSecurity {
+fun MailConnectionSecurity.toConnectionSecurity(): ConnectionSecurity {
return when (this) {
MailConnectionSecurity.NONE -> None
MailConnectionSecurity.STARTTLS_REQUIRED -> StartTLS
@@ -34,7 +34,7 @@ internal fun MailConnectionSecurity.toConnectionSecurity(): ConnectionSecurity {
}
@Suppress("MagicNumber")
-internal fun ConnectionSecurity.toSmtpDefaultPort(): Long {
+fun ConnectionSecurity.toSmtpDefaultPort(): Long {
return when (this) {
None -> 587
StartTLS -> 587
@@ -43,7 +43,7 @@ internal fun ConnectionSecurity.toSmtpDefaultPort(): Long {
}
@Suppress("MagicNumber")
-internal fun ConnectionSecurity.toImapDefaultPort(): Long {
+fun ConnectionSecurity.toImapDefaultPort(): Long {
return when (this) {
None -> 143
StartTLS -> 143
@@ -52,7 +52,7 @@ internal fun ConnectionSecurity.toImapDefaultPort(): Long {
}
@Suppress("MagicNumber")
-internal fun ConnectionSecurity.toPop3DefaultPort(): Long {
+fun ConnectionSecurity.toPop3DefaultPort(): Long {
return when (this) {
None -> 110
StartTLS -> 110
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingProtocolType.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/IncomingProtocolType.kt
similarity index 83%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingProtocolType.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/IncomingProtocolType.kt
index cc245cecd2b528437ba9211687c4329b6638dd81..62545c1ca082c4c24611ce8d3b80ceb4bbd6afae 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingProtocolType.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/IncomingProtocolType.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import kotlinx.collections.immutable.toImmutableList
@@ -21,7 +21,7 @@ enum class IncomingProtocolType(
}
}
-internal fun IncomingProtocolType.toDefaultPort(connectionSecurity: ConnectionSecurity): Long {
+fun IncomingProtocolType.toDefaultPort(connectionSecurity: ConnectionSecurity): Long {
return when (this) {
IncomingProtocolType.IMAP -> connectionSecurity.toImapDefaultPort()
IncomingProtocolType.POP3 -> connectionSecurity.toPop3DefaultPort()
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/MailConnectionSecurity.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/MailConnectionSecurity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2f011726fabbba8050120e89eeb0715b85e5b5e5
--- /dev/null
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/MailConnectionSecurity.kt
@@ -0,0 +1,3 @@
+package app.k9mail.feature.account.common.domain.entity
+
+typealias MailConnectionSecurity = com.fsck.k9.mail.ConnectionSecurity
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/OutgoingProtocolType.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/OutgoingProtocolType.kt
similarity index 68%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/OutgoingProtocolType.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/OutgoingProtocolType.kt
index c90cde0aba168b12a6c62e94e0ea0c2839b5f46c..e59b81a7fad9a4f588819b66b1911c5d9effed15 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/OutgoingProtocolType.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/OutgoingProtocolType.kt
@@ -1,8 +1,8 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import kotlinx.collections.immutable.toImmutableList
-internal enum class OutgoingProtocolType(
+enum class OutgoingProtocolType(
val defaultName: String,
val defaultConnectionSecurity: ConnectionSecurity,
) {
@@ -16,7 +16,7 @@ internal enum class OutgoingProtocolType(
}
}
-internal fun OutgoingProtocolType.toDefaultPort(connectionSecurity: ConnectionSecurity): Long {
+fun OutgoingProtocolType.toDefaultPort(connectionSecurity: ConnectionSecurity): Long {
return when (this) {
OutgoingProtocolType.SMTP -> connectionSecurity.toSmtpDefaultPort()
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/BooleanInputField.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/BooleanInputField.kt
similarity index 97%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/BooleanInputField.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/BooleanInputField.kt
index 561e56257228ef10fe01b8c1577e7dc2faac476a..273179c753ebc45bfb20246467c1f78c94c4339a 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/BooleanInputField.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/BooleanInputField.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.input
+package app.k9mail.feature.account.common.domain.input
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/InputField.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/InputField.kt
similarity index 96%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/InputField.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/InputField.kt
index 9aa75019fd3709e4857becc22147511cd015b5d8..d5d2925a61f290df07f2fee720f196c1646834db 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/InputField.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/InputField.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.input
+package app.k9mail.feature.account.common.domain.input
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/NumberInputField.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/NumberInputField.kt
similarity index 97%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/NumberInputField.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/NumberInputField.kt
index 34b8a825133119f27756b206564214c99fff99e5..966e18645fe5dfc1fc8977fa7063fa9ef097950f 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/NumberInputField.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/NumberInputField.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.input
+package app.k9mail.feature.account.common.domain.input
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/StringInputField.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/StringInputField.kt
similarity index 97%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/StringInputField.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/StringInputField.kt
index a793eb5320f4b7c348b5425880fe07fd0908e79e..96c1cc68cbac48136af77d17625bf46d8b747053 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/input/StringInputField.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/input/StringInputField.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.input
+package app.k9mail.feature.account.common.domain.input
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/AccountSetupTopAppBar.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AccountTopAppBar.kt
similarity index 65%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/AccountSetupTopAppBar.kt
rename to feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AccountTopAppBar.kt
index c5f07c409357e3fc40aef365e255d6af87970a4a..daced9fb93fe7350553bc4af839d86cd70b85679 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/AccountSetupTopAppBar.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AccountTopAppBar.kt
@@ -1,22 +1,25 @@
-package app.k9mail.feature.account.setup.ui.common
+package app.k9mail.feature.account.common.ui
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.k9mail.core.ui.compose.common.DevicePreviews
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBar
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
-import app.k9mail.feature.account.setup.R.string
+import app.k9mail.feature.account.common.R
@Composable
-internal fun AccountSetupTopAppBar(
+fun AccountTopAppBar(
title: String,
+ modifier: Modifier = Modifier,
) {
TopAppBar(
title = title,
- subtitle = stringResource(id = string.account_setup_title),
+ modifier = modifier,
+ subtitle = stringResource(id = R.string.account_common_title),
titleContentPadding = PaddingValues(
start = MainTheme.spacings.double,
),
@@ -25,9 +28,9 @@ internal fun AccountSetupTopAppBar(
@DevicePreviews
@Composable
-internal fun AccountSetupTopAppBarK9Preview() {
+internal fun AccountTopAppBarK9Preview() {
K9Theme {
- AccountSetupTopAppBar(
+ AccountTopAppBar(
title = "Title",
)
}
@@ -35,9 +38,9 @@ internal fun AccountSetupTopAppBarK9Preview() {
@DevicePreviews
@Composable
-internal fun AccountSetupTopAppBarThunderbirdPreview() {
+internal fun AccountTopAppBarThunderbirdPreview() {
ThunderbirdTheme {
- AccountSetupTopAppBar(
+ AccountTopAppBar(
title = "Title",
)
}
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AppTitleTopHeader.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AppTitleTopHeader.kt
index 13fde1bf9d339833ebb9804a00e8ebd204523f2a..ad0818602dffa63407df38e2d49db3bc9e39eae3 100644
--- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AppTitleTopHeader.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/AppTitleTopHeader.kt
@@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
@@ -18,11 +19,12 @@ import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadline2
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
+import app.k9mail.feature.account.common.R
@Composable
fun AppTitleTopHeader(
- title: String,
modifier: Modifier = Modifier,
+ title: String = stringResource(id = R.string.account_common_title),
) {
ResponsiveWidthContainer(
modifier = Modifier
@@ -64,6 +66,6 @@ fun AppTitleTopHeader(
@Composable
internal fun AppTitleTopHeaderPreview() {
PreviewWithThemes {
- AppTitleTopHeader("Title")
+ AppTitleTopHeader(title = "Title")
}
}
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/WizardNavigationBar.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/WizardNavigationBar.kt
index f2e625d254f122245919772699df8476561b5d9a..1f50e2884b7307dbdd18de7a907bf3c042a2bab8 100644
--- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/WizardNavigationBar.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/WizardNavigationBar.kt
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import app.k9mail.core.ui.compose.common.DevicePreviews
import app.k9mail.core.ui.compose.designsystem.atom.button.Button
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonOutlined
@@ -13,14 +14,15 @@ import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
+import app.k9mail.feature.account.common.R
@Composable
fun WizardNavigationBar(
- nextButtonText: String,
- backButtonText: String,
onNextClick: () -> Unit,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
+ nextButtonText: String = stringResource(id = R.string.account_common_button_next),
+ backButtonText: String = stringResource(id = R.string.account_common_button_back),
state: WizardNavigationBarState = WizardNavigationBarState(),
) {
ResponsiveWidthContainer(
@@ -72,8 +74,6 @@ private fun getHorizontalArrangement(state: WizardNavigationBarState): Arrangeme
internal fun WizardNavigationBarK9Preview() {
K9Theme {
WizardNavigationBar(
- nextButtonText = "Next",
- backButtonText = "Back",
onNextClick = {},
onBackClick = {},
)
@@ -85,8 +85,6 @@ internal fun WizardNavigationBarK9Preview() {
internal fun WizardNavigationBarPreview() {
PreviewWithThemes {
WizardNavigationBar(
- nextButtonText = "Next",
- backButtonText = "Back",
onNextClick = {},
onBackClick = {},
)
@@ -98,8 +96,6 @@ internal fun WizardNavigationBarPreview() {
internal fun WizardNavigationBarDisabledPreview() {
PreviewWithThemes {
WizardNavigationBar(
- nextButtonText = "Next",
- backButtonText = "Back",
onNextClick = {},
onBackClick = {},
state = WizardNavigationBarState(
@@ -115,8 +111,6 @@ internal fun WizardNavigationBarDisabledPreview() {
internal fun WizardNavigationBarHideNextPreview() {
PreviewWithThemes {
WizardNavigationBar(
- nextButtonText = "Next",
- backButtonText = "Back",
onNextClick = {},
onBackClick = {},
state = WizardNavigationBarState(
@@ -131,8 +125,6 @@ internal fun WizardNavigationBarHideNextPreview() {
internal fun WizardNavigationBarHideBackPreview() {
PreviewWithThemes {
WizardNavigationBar(
- nextButtonText = "Next",
- backButtonText = "Back",
onNextClick = {},
onBackClick = {},
state = WizardNavigationBarState(
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e04aada6a44f5659471521556eea352a9177d1be
--- /dev/null
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt
@@ -0,0 +1,50 @@
+package app.k9mail.feature.account.common.ui.preview
+
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.ServerSettings
+
+class PreviewAccountStateRepository : AccountDomainContract.AccountStateRepository {
+
+ override fun getState(): AccountState = AccountState(
+ emailAddress = "test@example.com",
+ incomingServerSettings = ServerSettings(
+ type = "imap",
+ host = "imap.example.com",
+ port = 993,
+ connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
+ authenticationType = AuthType.PLAIN,
+ username = "test",
+ password = "password",
+ clientCertificateAlias = null,
+ ),
+ outgoingServerSettings = ServerSettings(
+ type = "smtp",
+ host = "smtp.example.com",
+ port = 465,
+ connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
+ authenticationType = AuthType.PLAIN,
+ username = "test",
+ password = "password",
+ clientCertificateAlias = null,
+ ),
+ )
+
+ override fun setState(accountState: AccountState) = Unit
+
+ override fun setEmailAddress(emailAddress: String) = Unit
+
+ override fun setIncomingServerSettings(serverSettings: ServerSettings) = Unit
+
+ override fun setOutgoingServerSettings(serverSettings: ServerSettings) = Unit
+
+ override fun setAuthorizationState(authorizationState: AuthorizationState) = Unit
+
+ override fun setOptions(options: AccountOptions) = Unit
+
+ override fun clear() = Unit
+}
diff --git a/feature/account/common/src/main/res/values/strings.xml b/feature/account/common/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ecff2a1a322e18d46b993c9d45fd87c1ad591da5
--- /dev/null
+++ b/feature/account/common/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+ K-9 Mail
+ Next
+ Back
+
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/data/InMemoryAccountSetupStateRepositoryTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt
similarity index 69%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/data/InMemoryAccountSetupStateRepositoryTest.kt
rename to feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt
index a234017f2b14dc7362460a93bab5d4fa6584bb1c..5d52ec4935206da55d7f4455fb0b92937fc261e9 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/data/InMemoryAccountSetupStateRepositoryTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt
@@ -1,8 +1,8 @@
-package app.k9mail.feature.account.setup.data
+package app.k9mail.feature.account.common.data
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
@@ -10,16 +10,17 @@ import com.fsck.k9.mail.ConnectionSecurity
import com.fsck.k9.mail.ServerSettings
import org.junit.Test
-class InMemoryAccountSetupStateRepositoryTest {
+class InMemoryAccountStateRepositoryTest {
@Test
fun `should initialize with empty state`() {
- val testSubject = InMemoryAccountSetupStateRepository()
+ val testSubject = InMemoryAccountStateRepository()
val result = testSubject.getState()
assertThat(result).isEqualTo(
- AccountSetupState(
+ AccountState(
+ uuid = null,
emailAddress = null,
incomingServerSettings = null,
outgoingServerSettings = null,
@@ -30,9 +31,10 @@ class InMemoryAccountSetupStateRepositoryTest {
}
@Test
- fun `should save state`() {
- val testSubject = InMemoryAccountSetupStateRepository(
- AccountSetupState(
+ fun `should set state`() {
+ val testSubject = InMemoryAccountStateRepository(
+ AccountState(
+ uuid = "uuid",
emailAddress = "emailAddress",
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
@@ -40,7 +42,8 @@ class InMemoryAccountSetupStateRepositoryTest {
options = OPTIONS,
),
)
- val newState = AccountSetupState(
+ val newState = AccountState(
+ uuid = "uuid2",
emailAddress = "emailAddress2",
incomingServerSettings = INCOMING_SERVER_SETTINGS.copy(host = "imap2.example.org"),
outgoingServerSettings = OUTGOING_SERVER_SETTINGS.copy(host = "smtp2.example.org"),
@@ -55,56 +58,56 @@ class InMemoryAccountSetupStateRepositoryTest {
),
)
- testSubject.save(newState)
+ testSubject.setState(newState)
assertThat(testSubject.getState()).isEqualTo(newState)
}
@Test
- fun `should save email address`() {
- val testSubject = InMemoryAccountSetupStateRepository()
+ fun `should set email address`() {
+ val testSubject = InMemoryAccountStateRepository()
- testSubject.saveEmailAddress("emailAddress")
+ testSubject.setEmailAddress("emailAddress")
assertThat(testSubject.getState().emailAddress)
.isEqualTo("emailAddress")
}
@Test
- fun `should save incoming server settings`() {
- val testSubject = InMemoryAccountSetupStateRepository()
+ fun `should set incoming server settings`() {
+ val testSubject = InMemoryAccountStateRepository()
- testSubject.saveIncomingServerSettings(INCOMING_SERVER_SETTINGS)
+ testSubject.setIncomingServerSettings(INCOMING_SERVER_SETTINGS)
assertThat(testSubject.getState().incomingServerSettings)
.isEqualTo(INCOMING_SERVER_SETTINGS)
}
@Test
- fun `should save outgoing server settings`() {
- val testSubject = InMemoryAccountSetupStateRepository()
+ fun `should set outgoing server settings`() {
+ val testSubject = InMemoryAccountStateRepository()
- testSubject.saveOutgoingServerSettings(OUTGOING_SERVER_SETTINGS)
+ testSubject.setOutgoingServerSettings(OUTGOING_SERVER_SETTINGS)
assertThat(testSubject.getState().outgoingServerSettings)
.isEqualTo(OUTGOING_SERVER_SETTINGS)
}
@Test
- fun `should save authorization state`() {
- val testSubject = InMemoryAccountSetupStateRepository()
+ fun `should set authorization state`() {
+ val testSubject = InMemoryAccountStateRepository()
- testSubject.saveAuthorizationState(AuthorizationState("authorizationState"))
+ testSubject.setAuthorizationState(AuthorizationState("authorizationState"))
assertThat(testSubject.getState().authorizationState)
.isEqualTo(AuthorizationState("authorizationState"))
}
@Test
- fun `should save options`() {
- val testSubject = InMemoryAccountSetupStateRepository()
+ fun `should set options`() {
+ val testSubject = InMemoryAccountStateRepository()
- testSubject.saveOptions(OPTIONS)
+ testSubject.setOptions(OPTIONS)
assertThat(testSubject.getState().options)
.isEqualTo(OPTIONS)
@@ -112,8 +115,9 @@ class InMemoryAccountSetupStateRepositoryTest {
@Test
fun `should clear state`() {
- val testSubject = InMemoryAccountSetupStateRepository(
- AccountSetupState(
+ val testSubject = InMemoryAccountStateRepository(
+ AccountState(
+ uuid = "uuid",
emailAddress = "emailAddress",
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
@@ -125,7 +129,8 @@ class InMemoryAccountSetupStateRepositoryTest {
testSubject.clear()
assertThat(testSubject.getState()).isEqualTo(
- AccountSetupState(
+ AccountState(
+ uuid = null,
emailAddress = null,
incomingServerSettings = null,
outgoingServerSettings = null,
diff --git a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dd3c0c4e9759b0d30635a9fa8458d05d2c2da0c2
--- /dev/null
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt
@@ -0,0 +1,23 @@
+package app.k9mail.feature.account.common.domain.entity
+
+import assertk.all
+import assertk.assertThat
+import assertk.assertions.isNull
+import assertk.assertions.prop
+import org.junit.Test
+
+class AccountStateTest {
+
+ @Test
+ fun `should default to null state`() {
+ val accountState = AccountState()
+
+ assertThat(accountState).all {
+ prop(AccountState::emailAddress).isNull()
+ prop(AccountState::incomingServerSettings).isNull()
+ prop(AccountState::outgoingServerSettings).isNull()
+ prop(AccountState::authorizationState).isNull()
+ prop(AccountState::options).isNull()
+ }
+ }
+}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AuthenticationTypeTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt
similarity index 96%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AuthenticationTypeTest.kt
rename to feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt
index 72dc05926196ce7338e94a1454f2de92b6b22a1d..f7f3449927078c8e4a46a8ed85d52ae3731899de 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AuthenticationTypeTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import assertk.assertThat
import assertk.assertions.isEqualTo
diff --git a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1311b660768910761c5a6addb463a3826c196c80
--- /dev/null
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt
@@ -0,0 +1,15 @@
+package app.k9mail.feature.account.common.domain.entity
+
+import assertk.assertThat
+import assertk.assertions.isNull
+import org.junit.Test
+
+class AuthorizationStateTest {
+
+ @Test
+ fun `should default to null state`() {
+ val authorizationState = AuthorizationState()
+
+ assertThat(authorizationState.state).isNull()
+ }
+}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/ConnectionSecurityTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/ConnectionSecurityTest.kt
similarity index 98%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/ConnectionSecurityTest.kt
rename to feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/ConnectionSecurityTest.kt
index 0f74a657ca524d0e794d0ecbdc2c533eb685ed6f..4cdc873c1043f1278c2f5d23bd8adabc951eb536 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/ConnectionSecurityTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/ConnectionSecurityTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import assertk.assertThat
import assertk.assertions.isEqualTo
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingProtocolTypeTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/IncomingProtocolTypeTest.kt
similarity index 96%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingProtocolTypeTest.kt
rename to feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/IncomingProtocolTypeTest.kt
index d10da4c8faa494bb6a7e6b8b23b10dcea17c28bb..9966eb14a30da8914e84e46da168978bd57b7de6 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingProtocolTypeTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/IncomingProtocolTypeTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import assertk.assertThat
import assertk.assertions.isEqualTo
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/OutgoingProtocolTypeTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/OutgoingProtocolTypeTest.kt
similarity index 95%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/OutgoingProtocolTypeTest.kt
rename to feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/OutgoingProtocolTypeTest.kt
index 33afc43d1ad325bef557344232b9a7ba9aa32658..15dca7520c4b02af337d4d9c5668274406ac044d 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/OutgoingProtocolTypeTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/OutgoingProtocolTypeTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.common.domain.entity
import assertk.assertThat
import assertk.assertions.isEqualTo
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/input/InputFieldTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/input/InputFieldTest.kt
similarity index 99%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/input/InputFieldTest.kt
rename to feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/input/InputFieldTest.kt
index af3ae423eaa2e89040cd87d07ec7f18c70093887..056edec7f81d65618471e4f914a9a50a43d3fd75 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/input/InputFieldTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/input/InputFieldTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.input
+package app.k9mail.feature.account.common.domain.input
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
diff --git a/feature/account/edit/build.gradle.kts b/feature/account/edit/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..5ef5291d048fb4efd41f663bb559077368ceb059
--- /dev/null
+++ b/feature/account/edit/build.gradle.kts
@@ -0,0 +1,32 @@
+plugins {
+ id(ThunderbirdPlugins.Library.androidCompose)
+}
+
+android {
+ namespace = "app.k9mail.feature.account.edit"
+ resourcePrefix = "account_edit_"
+
+ buildTypes {
+ debug {
+ manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
+ }
+ release {
+ manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
+ }
+ }
+}
+
+dependencies {
+ implementation(projects.core.ui.compose.designsystem)
+ implementation(projects.core.common)
+
+ implementation(projects.mail.common)
+
+ implementation(projects.feature.account.common)
+ implementation(projects.feature.account.oauth)
+ implementation(projects.feature.account.server.settings)
+ implementation(projects.feature.account.server.certificate)
+ implementation(projects.feature.account.server.validation)
+
+ testImplementation(projects.core.ui.compose.testing)
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/AccountEditExternalContract.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/AccountEditExternalContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d7c65cd0e101752b688c9b08f29e3b74e6eebaa9
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/AccountEditExternalContract.kt
@@ -0,0 +1,15 @@
+package app.k9mail.feature.account.edit
+
+import app.k9mail.feature.account.common.domain.entity.Account
+
+interface AccountEditExternalContract {
+
+ fun interface AccountUpdater {
+ suspend fun updateAccount(account: Account): AccountUpdaterResult
+
+ sealed interface AccountUpdaterResult {
+ data class Success(val accountId: String) : AccountUpdaterResult
+ data class Error(val message: String) : AccountUpdaterResult
+ }
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/AccountEditModule.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/AccountEditModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ca91aa4b0f0981f14d297758da6ee812ff66e6f7
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/AccountEditModule.kt
@@ -0,0 +1,48 @@
+package app.k9mail.feature.account.edit
+
+import app.k9mail.feature.account.common.featureAccountCommonModule
+import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
+import app.k9mail.feature.account.edit.domain.usecase.LoadAccountState
+import app.k9mail.feature.account.edit.ui.EditIncomingServerSettingsViewModel
+import app.k9mail.feature.account.edit.ui.EditOutgoingServerSettingsViewModel
+import app.k9mail.feature.account.oauth.featureAccountOAuthModule
+import app.k9mail.feature.account.server.certificate.featureAccountServerCertificateModule
+import app.k9mail.feature.account.server.settings.featureAccountServerSettingsModule
+import app.k9mail.feature.account.server.validation.featureAccountServerValidationModule
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.dsl.module
+
+val featureAccountEditModule = module {
+ includes(
+ featureAccountCommonModule,
+ featureAccountOAuthModule,
+ featureAccountServerCertificateModule,
+ featureAccountServerSettingsModule,
+ featureAccountServerValidationModule,
+ )
+
+ factory {
+ LoadAccountState(
+ accountStateLoader = get(),
+ accountStateRepository = get(),
+ )
+ }
+
+ viewModel { (accountUuid: String) ->
+ EditIncomingServerSettingsViewModel(
+ accountUuid = accountUuid,
+ accountStateLoader = get(),
+ validator = get(),
+ accountStateRepository = get(),
+ )
+ }
+
+ viewModel { (accountUuid: String) ->
+ EditOutgoingServerSettingsViewModel(
+ accountUuid = accountUuid,
+ accountStateLoader = get(),
+ validator = get(),
+ accountStateRepository = get(),
+ )
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/domain/AccountEditDomainContract.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/domain/AccountEditDomainContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..58dd693013232b5a4e12ef1afe5e068bc3e86ffa
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/domain/AccountEditDomainContract.kt
@@ -0,0 +1,13 @@
+package app.k9mail.feature.account.edit.domain
+
+import app.k9mail.feature.account.common.domain.entity.AccountState
+
+interface AccountEditDomainContract {
+
+ interface UseCase {
+
+ fun interface LoadAccountState {
+ suspend fun execute(accountUuid: String): AccountState
+ }
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountState.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountState.kt
new file mode 100644
index 0000000000000000000000000000000000000000..832fe6d9347d6e6ed77bd0b41bbe36447ccf5a05
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountState.kt
@@ -0,0 +1,29 @@
+package app.k9mail.feature.account.edit.domain.usecase
+
+import app.k9mail.feature.account.common.AccountCommonExternalContract
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
+
+class LoadAccountState(
+ private val accountStateLoader: AccountCommonExternalContract.AccountStateLoader,
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
+) : AccountEditDomainContract.UseCase.LoadAccountState {
+ override suspend fun execute(accountUuid: String): AccountState {
+ val accountState = accountStateRepository.getState()
+ return if (accountState.uuid == accountUuid) {
+ accountState
+ } else {
+ loadState(accountUuid)
+ }
+ }
+
+ private suspend fun loadState(accountUuid: String): AccountState {
+ val accountState = accountStateLoader.loadAccountState(accountUuid)
+ return if (accountState != null) {
+ accountState
+ } else {
+ AccountState(uuid = accountUuid)
+ }.also { accountStateRepository.setState(it) }
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/navigation/AccountEditNavigation.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/navigation/AccountEditNavigation.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2692bd103f7f16add1dc9db68e778d150d02497a
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/navigation/AccountEditNavigation.kt
@@ -0,0 +1,70 @@
+package app.k9mail.feature.account.edit.navigation
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import app.k9mail.core.ui.compose.common.navigation.deepLinkComposable
+import app.k9mail.core.ui.compose.common.navigation.getStringArgument
+import app.k9mail.feature.account.edit.ui.EditIncomingServerSettingsNavHost
+import app.k9mail.feature.account.edit.ui.EditOutgoingServerSettingsNavHost
+
+internal const val ARGUMENT_ACCOUNT_UUID = "accountUuid"
+
+const val NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_INCOMING = "account/edit/config/incoming/{accountUuid}"
+const val NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_OUTGOING = "account/edit/config/outgoing/{accountUuid}"
+
+fun NavController.navigateToAccountEditConfigIncoming(accountUuid: String) {
+ navigate(
+ route = NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_INCOMING.withAccountUuid(accountUuid),
+ )
+}
+
+fun NavController.navigateToAccountEditConfigOutgoing(accountUuid: String) {
+ navigate(
+ route = NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_OUTGOING.withAccountUuid(accountUuid),
+ )
+}
+
+fun NavGraphBuilder.accountEditRoute(
+ onBack: () -> Unit,
+ onFinish: () -> Unit,
+) {
+ deepLinkComposable(
+ route = NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_INCOMING,
+ arguments = listOf(
+ navArgument(ARGUMENT_ACCOUNT_UUID) {
+ type = NavType.StringType
+ },
+ ),
+ ) { backStackEntry ->
+ val accountUuid = backStackEntry.getStringArgument(ARGUMENT_ACCOUNT_UUID)
+ EditIncomingServerSettingsNavHost(
+ accountUuid = accountUuid,
+ onFinish = { onFinish() },
+ onBack = onBack,
+ )
+ }
+ deepLinkComposable(
+ route = NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_OUTGOING,
+ arguments = listOf(
+ navArgument(ARGUMENT_ACCOUNT_UUID) {
+ type = NavType.StringType
+ },
+ ),
+ ) { backStackEntry ->
+ val accountUuid = backStackEntry.getStringArgument(ARGUMENT_ACCOUNT_UUID)
+ EditOutgoingServerSettingsNavHost(
+ accountUuid = accountUuid,
+ onFinish = { onFinish() },
+ onBack = onBack,
+ )
+ }
+}
+
+fun String.withAccountUuid(accountUuid: String): String {
+ return replace(
+ oldValue = "{$ARGUMENT_ACCOUNT_UUID}",
+ newValue = accountUuid,
+ )
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsNavHost.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsNavHost.kt
new file mode 100644
index 0000000000000000000000000000000000000000..329fdceee96a5bccc3fd643e609634e94ee5e825
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsNavHost.kt
@@ -0,0 +1,52 @@
+package app.k9mail.feature.account.edit.ui
+
+import androidx.compose.runtime.Composable
+import androidx.navigation.NavController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsScreen
+import app.k9mail.feature.account.server.validation.ui.IncomingServerValidationViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationScreen
+import org.koin.androidx.compose.koinViewModel
+import org.koin.core.parameter.parametersOf
+
+private const val NESTED_NAVIGATION_ROUTE_CONFIG = "config"
+private const val NESTED_NAVIGATION_ROUTE_VALIDATION = "validation"
+
+private fun NavController.navigateToValidation() {
+ navigate(NESTED_NAVIGATION_ROUTE_VALIDATION)
+}
+
+@Composable
+fun EditIncomingServerSettingsNavHost(
+ accountUuid: String,
+ onFinish: () -> Unit,
+ onBack: () -> Unit,
+) {
+ val navController = rememberNavController()
+
+ NavHost(
+ navController = navController,
+ startDestination = NESTED_NAVIGATION_ROUTE_CONFIG,
+ ) {
+ composable(route = NESTED_NAVIGATION_ROUTE_CONFIG) {
+ IncomingServerSettingsScreen(
+ onBack = onBack,
+ onNext = { navController.navigateToValidation() },
+ viewModel = koinViewModel {
+ parametersOf(accountUuid)
+ },
+ )
+ }
+ composable(route = NESTED_NAVIGATION_ROUTE_VALIDATION) {
+ ServerValidationScreen(
+ onBack = { navController.popBackStack() },
+ onNext = onFinish,
+ viewModel = koinViewModel {
+ parametersOf(accountUuid)
+ },
+ )
+ }
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsViewModel.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..23a04c604a7a931e5c866f8218b6db47ec3c4149
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsViewModel.kt
@@ -0,0 +1,32 @@
+package app.k9mail.feature.account.edit.ui
+
+import androidx.lifecycle.viewModelScope
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsViewModel
+import app.k9mail.feature.account.server.settings.ui.incoming.toIncomingConfigState
+import kotlinx.coroutines.launch
+
+class EditIncomingServerSettingsViewModel(
+ val accountUuid: String,
+ private val accountStateLoader: AccountEditDomainContract.UseCase.LoadAccountState,
+ validator: IncomingServerSettingsContract.Validator,
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ initialState: IncomingServerSettingsContract.State = IncomingServerSettingsContract.State(),
+) : IncomingServerSettingsViewModel(
+ validator = validator,
+ accountStateRepository = accountStateRepository,
+ initialState = initialState,
+) {
+
+ override fun loadAccountState() {
+ viewModelScope.launch {
+ val state = accountStateLoader.execute(accountUuid)
+
+ updateState {
+ state.toIncomingConfigState()
+ }
+ }
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsNavHost.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsNavHost.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5b6d5ed5e7d37227b9c59c5e28782c1bbacf0bf9
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsNavHost.kt
@@ -0,0 +1,53 @@
+package app.k9mail.feature.account.edit.ui
+
+import androidx.compose.runtime.Composable
+import androidx.navigation.NavController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsScreen
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsViewModel
+import app.k9mail.feature.account.server.validation.ui.OutgoingServerValidationViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationScreen
+import org.koin.androidx.compose.koinViewModel
+import org.koin.core.parameter.parametersOf
+
+private const val NESTED_NAVIGATION_ROUTE_CONFIG = "config"
+private const val NESTED_NAVIGATION_ROUTE_VALIDATION = "validation"
+
+private fun NavController.navigateToValidation() {
+ navigate(NESTED_NAVIGATION_ROUTE_VALIDATION)
+}
+
+@Composable
+fun EditOutgoingServerSettingsNavHost(
+ accountUuid: String,
+ onFinish: () -> Unit,
+ onBack: () -> Unit,
+) {
+ val navController = rememberNavController()
+
+ NavHost(
+ navController = navController,
+ startDestination = NESTED_NAVIGATION_ROUTE_CONFIG,
+ ) {
+ composable(route = NESTED_NAVIGATION_ROUTE_CONFIG) {
+ OutgoingServerSettingsScreen(
+ onBack = onBack,
+ onNext = { navController.navigateToValidation() },
+ viewModel = koinViewModel {
+ parametersOf(accountUuid)
+ },
+ )
+ }
+ composable(route = NESTED_NAVIGATION_ROUTE_VALIDATION) {
+ ServerValidationScreen(
+ onBack = { navController.popBackStack() },
+ onNext = onFinish,
+ viewModel = koinViewModel {
+ parametersOf(accountUuid)
+ },
+ )
+ }
+ }
+}
diff --git a/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsViewModel.kt b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..67453cbe0d0a525eb836f6ae326e20c6ccd1bb54
--- /dev/null
+++ b/feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsViewModel.kt
@@ -0,0 +1,31 @@
+package app.k9mail.feature.account.edit.ui
+
+import androidx.lifecycle.viewModelScope
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsViewModel
+import app.k9mail.feature.account.server.settings.ui.outgoing.toOutgoingConfigState
+import kotlinx.coroutines.launch
+
+class EditOutgoingServerSettingsViewModel(
+ val accountUuid: String,
+ private val accountStateLoader: AccountEditDomainContract.UseCase.LoadAccountState,
+ validator: OutgoingServerSettingsContract.Validator,
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ initialState: OutgoingServerSettingsContract.State = OutgoingServerSettingsContract.State(),
+) : OutgoingServerSettingsViewModel(
+ validator = validator,
+ accountStateRepository = accountStateRepository,
+ initialState = initialState,
+) {
+ override fun loadAccountState() {
+ viewModelScope.launch {
+ val state = accountStateLoader.execute(accountUuid)
+
+ updateState {
+ state.toOutgoingConfigState()
+ }
+ }
+ }
+}
diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/AccountEditModuleKtTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/AccountEditModuleKtTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..41954b7967270358122bf78b5130694720273678
--- /dev/null
+++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/AccountEditModuleKtTest.kt
@@ -0,0 +1,70 @@
+package app.k9mail.feature.account.edit
+
+import android.content.Context
+import app.k9mail.core.common.oauth.OAuthConfigurationFactory
+import app.k9mail.feature.account.common.AccountCommonExternalContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
+import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.mail.oauth.OAuth2TokenProviderFactory
+import com.fsck.k9.mail.ssl.LocalKeyStore
+import com.fsck.k9.mail.ssl.TrustedSocketFactory
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.annotation.KoinExperimentalAPI
+import org.koin.core.module.Module
+import org.koin.dsl.koinApplication
+import org.koin.dsl.module
+import org.koin.test.KoinTest
+import org.koin.test.check.checkModules
+import org.koin.test.verify.verify
+import org.mockito.Mockito
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+
+@OptIn(KoinExperimentalAPI::class)
+@RunWith(RobolectricTestRunner::class)
+class AccountEditModuleKtTest : KoinTest {
+
+ private val externalModule: Module = module {
+ single { Mockito.mock() }
+ single { Mockito.mock() }
+ single {
+ TrustedSocketFactory { _, _, _, _ -> null }
+ }
+ single { OAuthConfigurationFactory { emptyMap() } }
+ single {
+ OAuth2TokenProviderFactory { _ ->
+ object : OAuth2TokenProvider {
+ override fun getToken(timeoutMillis: Long) = TODO()
+ override fun invalidateToken() = TODO()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should have a valid di module`() {
+ featureAccountEditModule.verify(
+ extraTypes = listOf(
+ Context::class,
+ AccountState::class,
+ Class.forName("net.openid.appauth.AppAuthConfiguration").kotlin,
+ ServerValidationContract.State::class,
+ ServerCertificateErrorContract.State::class,
+ IncomingServerSettingsContract.State::class,
+ OutgoingServerSettingsContract.State::class,
+ ),
+ )
+
+ koinApplication {
+ modules(externalModule, featureAccountEditModule)
+ androidContext(RuntimeEnvironment.getApplication())
+ checkModules()
+ }
+ }
+}
diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9199820695a4b2076b744b4d17cbd50d2fef135f
--- /dev/null
+++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt
@@ -0,0 +1,115 @@
+package app.k9mail.feature.account.edit.domain.usecase
+
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.ServerSettings
+import kotlin.test.DefaultAsserter.fail
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class LoadAccountStateTest {
+
+ @Test
+ fun `should load account state WHEN account in state repository has different UUID`() = runTest {
+ val testSubject = LoadAccountState(
+ accountStateLoader = { _ ->
+ ACCOUNT_STATE
+ },
+ accountStateRepository = InMemoryAccountStateRepository(
+ state = ACCOUNT_STATE.copy(uuid = "differentUuid"),
+ ),
+ )
+
+ val result = testSubject.execute(ACCOUNT_UUID)
+
+ assertThat(result).isEqualTo(ACCOUNT_STATE)
+ }
+
+ @Test
+ fun `should return account state WHEN account in state repository has same UUID`() = runTest {
+ val testSubject = LoadAccountState(
+ accountStateLoader = { _ ->
+ fail("AccountStateLoader should not be called in this test")
+ },
+ accountStateRepository = InMemoryAccountStateRepository(
+ state = ACCOUNT_STATE,
+ ),
+ )
+
+ val result = testSubject.execute(ACCOUNT_UUID)
+
+ assertThat(result).isEqualTo(ACCOUNT_STATE)
+ }
+
+ @Test
+ fun `should return empty account state WHEN account loader returns null`() = runTest {
+ val testSubject = LoadAccountState(
+ accountStateLoader = { null },
+ accountStateRepository = InMemoryAccountStateRepository(),
+ )
+
+ val result = testSubject.execute(ACCOUNT_UUID)
+
+ assertThat(result).isEqualTo(
+ AccountState(
+ uuid = ACCOUNT_UUID,
+ emailAddress = null,
+ incomingServerSettings = null,
+ outgoingServerSettings = null,
+ authorizationState = null,
+ options = null,
+ ),
+ )
+ }
+
+ private companion object {
+ const val ACCOUNT_UUID = "accountUuid"
+ const val EMAIL_ADDRESS = "test@example.com"
+ val INCOMING_SERVER_SETTINGS = ServerSettings(
+ type = "imap",
+ host = "imap.example.com",
+ port = 993,
+ connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
+ authenticationType = AuthType.PLAIN,
+ username = "user",
+ password = "password",
+ clientCertificateAlias = null,
+ )
+ val OUTGOING_SERVER_SETTINGS = ServerSettings(
+ type = "smtp",
+ host = "smtp.example.com",
+ port = 465,
+ connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
+ authenticationType = AuthType.PLAIN,
+ username = "user",
+ password = "password",
+ clientCertificateAlias = null,
+ )
+
+ val AUTHORIZATION_STATE = AuthorizationState("authorization state")
+
+ val OPTIONS = AccountOptions(
+ accountName = "accountName",
+ displayName = "displayName",
+ emailSignature = null,
+ checkFrequencyInMinutes = 15,
+ messageDisplayCount = 25,
+ showNotification = true,
+ )
+
+ val ACCOUNT_STATE = AccountState(
+ uuid = ACCOUNT_UUID,
+ emailAddress = EMAIL_ADDRESS,
+ incomingServerSettings = INCOMING_SERVER_SETTINGS,
+ outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
+ authorizationState = AUTHORIZATION_STATE,
+ options = OPTIONS,
+ )
+ }
+}
diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsViewModelTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsViewModelTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..be2c90395a2d95bde8ace887c618f747872acc00
--- /dev/null
+++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/ui/EditIncomingServerSettingsViewModelTest.kt
@@ -0,0 +1,78 @@
+package app.k9mail.feature.account.edit.ui
+
+import app.k9mail.core.ui.compose.testing.MainDispatcherRule
+import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
+import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.incoming.fake.FakeIncomingServerSettingsValidator
+import assertk.assertions.isEqualTo
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.ServerSettings
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+
+class EditIncomingServerSettingsViewModelTest {
+
+ @get:Rule
+ val mainDispatcherRule = MainDispatcherRule()
+
+ @Test
+ fun `should load account state from use case`() = runTest {
+ val accountUuid = "accountUuid"
+ val accountState = AccountState(
+ uuid = "accountUuid",
+ emailAddress = "test@example.com",
+ incomingServerSettings = ServerSettings(
+ "imap",
+ "imap.example.com",
+ 123,
+ MailConnectionSecurity.SSL_TLS_REQUIRED,
+ AuthType.PLAIN,
+ "username",
+ "password",
+ clientCertificateAlias = null,
+ extra = emptyMap(),
+ ),
+ )
+
+ val testSubject = EditIncomingServerSettingsViewModel(
+ accountUuid = accountUuid,
+ accountStateLoader = { _ ->
+ delay(50)
+ accountState
+ },
+ validator = FakeIncomingServerSettingsValidator(),
+ accountStateRepository = InMemoryAccountStateRepository(),
+ initialState = State(),
+ )
+ val turbines = turbinesWithInitialStateCheck(testSubject, State())
+
+ testSubject.event(Event.LoadAccountState)
+
+ assertThatAndMviTurbinesConsumed(
+ actual = turbines.awaitStateItem(),
+ turbines = turbines,
+ ) {
+ isEqualTo(
+ State(
+ server = StringInputField(value = "imap.example.com"),
+ security = ConnectionSecurity.TLS,
+ port = NumberInputField(value = 123L),
+ authenticationType = AuthenticationType.PasswordCleartext,
+ username = StringInputField(value = "username"),
+ password = StringInputField(value = "password"),
+ ),
+ )
+ }
+ }
+}
diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsViewModelTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsViewModelTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2ebfd45d58f0bcd463127a9a5bb715090333c16e
--- /dev/null
+++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/ui/EditOutgoingServerSettingsViewModelTest.kt
@@ -0,0 +1,77 @@
+package app.k9mail.feature.account.edit.ui
+
+import app.k9mail.core.ui.compose.testing.MainDispatcherRule
+import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
+import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.outgoing.fake.FakeOutgoingServerSettingsValidator
+import assertk.assertions.isEqualTo
+import com.fsck.k9.mail.AuthType
+import com.fsck.k9.mail.ServerSettings
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+
+class EditOutgoingServerSettingsViewModelTest {
+
+ @get:Rule
+ val mainDispatcherRule = MainDispatcherRule()
+
+ @Test
+ fun `should load account state from use case`() = runTest {
+ val accountUuid = "accountUuid"
+ val accountState = AccountState(
+ uuid = "accountUuid",
+ emailAddress = "test@example.com",
+ outgoingServerSettings = ServerSettings(
+ "smtp",
+ "smtp.example.com",
+ 123,
+ MailConnectionSecurity.SSL_TLS_REQUIRED,
+ AuthType.PLAIN,
+ "username",
+ "password",
+ clientCertificateAlias = null,
+ extra = emptyMap(),
+ ),
+ )
+ val testSubject = EditOutgoingServerSettingsViewModel(
+ accountUuid = accountUuid,
+ accountStateLoader = { _ ->
+ delay(50)
+ accountState
+ },
+ validator = FakeOutgoingServerSettingsValidator(),
+ accountStateRepository = InMemoryAccountStateRepository(),
+ initialState = State(),
+ )
+ val turbines = turbinesWithInitialStateCheck(testSubject, State())
+
+ testSubject.event(Event.LoadAccountState)
+
+ assertThatAndMviTurbinesConsumed(
+ actual = turbines.awaitStateItem(),
+ turbines = turbines,
+ ) {
+ isEqualTo(
+ State(
+ server = StringInputField(value = "smtp.example.com"),
+ security = ConnectionSecurity.TLS,
+ port = NumberInputField(value = 123L),
+ authenticationType = AuthenticationType.PasswordCleartext,
+ username = StringInputField(value = "username"),
+ password = StringInputField(value = "password"),
+ ),
+ )
+ }
+ }
+}
diff --git a/feature/account/oauth/build.gradle.kts b/feature/account/oauth/build.gradle.kts
index 4b73b20bb40ad27098d08deebeb8d62c1ce4c3d7..23b5c43aa58e654e1e6250a519b7a532cce16dd4 100644
--- a/feature/account/oauth/build.gradle.kts
+++ b/feature/account/oauth/build.gradle.kts
@@ -19,7 +19,9 @@ android {
dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.common)
+
implementation(projects.mail.common)
+
implementation(projects.feature.account.common)
implementation(libs.appauth)
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/AccountOAuthModule.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/AccountOAuthModule.kt
index 23f1b90792e05190869dbf857f508e4085f8e8cb..e7df9c80d7d95d3c3d033cd8ffbdc05a4887bee9 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/AccountOAuthModule.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/AccountOAuthModule.kt
@@ -3,8 +3,8 @@ package app.k9mail.feature.account.oauth
import app.k9mail.core.common.coreCommonModule
import app.k9mail.feature.account.oauth.data.AuthorizationRepository
import app.k9mail.feature.account.oauth.data.AuthorizationStateRepository
-import app.k9mail.feature.account.oauth.domain.DomainContract
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase
import app.k9mail.feature.account.oauth.domain.usecase.CheckIsGoogleSignIn
import app.k9mail.feature.account.oauth.domain.usecase.FinishOAuthSignIn
import app.k9mail.feature.account.oauth.domain.usecase.GetOAuthRequestIntent
@@ -25,13 +25,13 @@ val featureAccountOAuthModule: Module = module {
)
}
- factory {
+ factory {
AuthorizationRepository(
service = get(),
)
}
- factory {
+ factory {
AuthorizationStateRepository()
}
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt
index 33c9b8c37c05e7346ae2a3a477d1d30ee75d3b6d..daba4ec57881bf228e74587b2052d9b3ae9d90dd 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt
@@ -1,6 +1,6 @@
package app.k9mail.feature.account.oauth.data
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import net.openid.appauth.AuthState
import org.json.JSONException
import timber.log.Timber
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepository.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepository.kt
index 5617d36c28aa3ded4699a234c4843752daad6378..882ac1780ce6a6acbe3def895d196cbd06ffdb66 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepository.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepository.kt
@@ -3,7 +3,7 @@ package app.k9mail.feature.account.oauth.data
import android.content.Intent
import androidx.core.net.toUri
import app.k9mail.core.common.oauth.OAuthConfiguration
-import app.k9mail.feature.account.oauth.domain.DomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationIntentResult
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
import kotlin.coroutines.resume
@@ -14,12 +14,13 @@ import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationResponse
import net.openid.appauth.AuthorizationService
import net.openid.appauth.AuthorizationServiceConfiguration
+import net.openid.appauth.CodeVerifierUtil
import net.openid.appauth.ResponseTypeValues
import timber.log.Timber
class AuthorizationRepository(
private val service: AuthorizationService,
-) : DomainContract.AuthorizationRepository {
+) : AccountOAuthDomainContract.AuthorizationRepository {
override fun getAuthorizationRequestIntent(
configuration: OAuthConfiguration,
@@ -80,9 +81,11 @@ class AuthorizationRepository(
configuration.redirectUri.toUri(),
)
+ val codeVerifier = CodeVerifierUtil.generateRandomCodeVerifier()
+
val authRequest = authRequestBuilder
.setScope(configuration.scopes.joinToString(" "))
- .setCodeVerifier(null)
+ .setCodeVerifier(codeVerifier)
.setLoginHint(emailAddress)
.build()
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationStateRepository.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationStateRepository.kt
index 35ef90cc12b75530d254fc3ba5a11e1bf554d470..86976aebac5421b9020f94670adebf6d5ff4bab6 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationStateRepository.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationStateRepository.kt
@@ -1,9 +1,9 @@
package app.k9mail.feature.account.oauth.data
-import app.k9mail.feature.account.oauth.domain.DomainContract
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
-class AuthorizationStateRepository : DomainContract.AuthorizationStateRepository {
+class AuthorizationStateRepository : AccountOAuthDomainContract.AuthorizationStateRepository {
override fun isAuthorized(authorizationState: AuthorizationState): Boolean {
val authState = authorizationState.toAuthState()
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/DomainContract.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/AccountOAuthDomainContract.kt
similarity index 93%
rename from feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/DomainContract.kt
rename to feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/AccountOAuthDomainContract.kt
index 157695b39769f8f62d0962a475be020784c53656..2a456ccebe2016f11f47f2879319158a275d60a2 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/DomainContract.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/AccountOAuthDomainContract.kt
@@ -2,13 +2,13 @@ package app.k9mail.feature.account.oauth.domain
import android.content.Intent
import app.k9mail.core.common.oauth.OAuthConfiguration
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationIntentResult
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationResponse
-interface DomainContract {
+interface AccountOAuthDomainContract {
interface UseCase {
fun interface SuggestServerName {
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationResult.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationResult.kt
index 28cceab189f178412e0d0494b902a23a958ff07e..d8497d77b43fe60f5f4799aa1a88e71e90ad79a0 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationResult.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/AuthorizationResult.kt
@@ -1,5 +1,7 @@
package app.k9mail.feature.account.oauth.domain.entity
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+
sealed interface AuthorizationResult {
data class Success(
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/OAuthResult.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/OAuthResult.kt
index 9bc7407b238d30328415550759c477f66a76340e..34e6094786b85378c54ab78cd87d821d2364088e 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/OAuthResult.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/OAuthResult.kt
@@ -1,5 +1,7 @@
package app.k9mail.feature.account.oauth.domain.entity
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+
sealed interface OAuthResult {
data class Success(
val authorizationState: AuthorizationState,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/ServerSettingsExtension.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/ServerSettingsExtension.kt
similarity index 73%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/ServerSettingsExtension.kt
rename to feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/ServerSettingsExtension.kt
index cc4425d19c85e85cfc0eac4f2aa5518211334943..8d0e7f638dbb667286c590b06e3bf022bda8d362 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/ServerSettingsExtension.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/entity/ServerSettingsExtension.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.oauth.domain.entity
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/CheckIsGoogleSignIn.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/CheckIsGoogleSignIn.kt
index c395e468d67949f3ca4703622ca00903fb55bf7f..7bc325e27c56c36a2ac03d60a4410b86f7a9e562 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/CheckIsGoogleSignIn.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/CheckIsGoogleSignIn.kt
@@ -1,6 +1,6 @@
package app.k9mail.feature.account.oauth.domain.usecase
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase
internal class CheckIsGoogleSignIn : UseCase.CheckIsGoogleSignIn {
override fun execute(hostname: String): Boolean {
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignIn.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignIn.kt
index 8b22d8fa04ef45866e58902cc676d629cdf98880..6257d107e278d21e064d20ab674ef127ddea1d6d 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignIn.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignIn.kt
@@ -1,12 +1,12 @@
package app.k9mail.feature.account.oauth.domain.usecase
import android.content.Intent
-import app.k9mail.feature.account.oauth.domain.DomainContract
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
class FinishOAuthSignIn(
- private val repository: DomainContract.AuthorizationRepository,
+ private val repository: AccountOAuthDomainContract.AuthorizationRepository,
) : UseCase.FinishOAuthSignIn {
override suspend fun execute(intent: Intent): AuthorizationResult {
val response = repository.getAuthorizationResponse(intent)
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/GetOAuthRequestIntent.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/GetOAuthRequestIntent.kt
index ac8ad0f03cd2239541d1409e190c3e6059ea4988..0d38a05132b6fd11c9822faae95aac8b639c007c 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/GetOAuthRequestIntent.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/GetOAuthRequestIntent.kt
@@ -1,12 +1,12 @@
package app.k9mail.feature.account.oauth.domain.usecase
import app.k9mail.core.common.oauth.OAuthConfigurationProvider
-import app.k9mail.feature.account.oauth.domain.DomainContract
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase.GetOAuthRequestIntent
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase.GetOAuthRequestIntent
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationIntentResult
internal class GetOAuthRequestIntent(
- private val repository: DomainContract.AuthorizationRepository,
+ private val repository: AccountOAuthDomainContract.AuthorizationRepository,
private val configurationProvider: OAuthConfigurationProvider,
) : GetOAuthRequestIntent {
override fun execute(hostname: String, emailAddress: String): AuthorizationIntentResult {
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/SuggestServerName.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/SuggestServerName.kt
index 6125730642aa6ceb57d63fb0f6f3a3ee6e1c7ea2..5744fba346f261f539d980bbba60627b382a720c 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/SuggestServerName.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/domain/usecase/SuggestServerName.kt
@@ -1,7 +1,7 @@
package app.k9mail.feature.account.oauth.domain.usecase
import app.k9mail.core.common.mail.Protocols
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase
@Deprecated("This is not needed anymore, remove once auth setup flow is updated")
class SuggestServerName : UseCase.SuggestServerName {
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthContract.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthContract.kt
index 6b704f8c30758edcccff379ae9085641f2ae5d09..ca807756b2a1619361b0eda787fae215af3f6bd5 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthContract.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthContract.kt
@@ -2,8 +2,8 @@ package app.k9mail.feature.account.oauth.ui
import android.content.Intent
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.common.ui.WizardNavigationBarState
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
interface AccountOAuthContract {
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModel.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModel.kt
index 7204837a2e035ec3c3ea226bda94c6310c978e03..8ea109d974a1dd227d576eb356d883b01e733dc5 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModel.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModel.kt
@@ -4,10 +4,10 @@ import android.app.Activity
import android.content.Intent
import androidx.lifecycle.viewModelScope
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.oauth.domain.DomainContract.UseCase
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract.UseCase
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationIntentResult
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Effect
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Error
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Event
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/FakeAccountOAuthViewModel.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/fake/FakeAccountOAuthViewModel.kt
similarity index 93%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/FakeAccountOAuthViewModel.kt
rename to feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/fake/FakeAccountOAuthViewModel.kt
index d76087a2acf063190838e04a079aaa9bbcb870c6..01f2ddb578ca925e4c1ab339f0e342bc62c5828c 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/FakeAccountOAuthViewModel.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/fake/FakeAccountOAuthViewModel.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui
+package app.k9mail.feature.account.oauth.ui.fake
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Effect
diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/view/SignInWithGoogleButton.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/view/SignInWithGoogleButton.kt
index 8a5ef0064ed807f1319e4cc8a4b30f018dd80d8f..e9529d9a2fd688d38173c4c0db13f9e45f4c7d37 100644
--- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/view/SignInWithGoogleButton.kt
+++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/ui/view/SignInWithGoogleButton.kt
@@ -3,22 +3,20 @@ package app.k9mail.feature.account.oauth.ui.view
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.ButtonElevation
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedButton
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -43,21 +41,19 @@ fun SignInWithGoogleButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
isLight: Boolean = MaterialTheme.colors.isLight,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: ButtonElevation? = ButtonDefaults.elevation(),
) {
- Surface(
- modifier = Modifier
- .border(
- width = 1.dp,
- color = getBorderColor(isLight),
- shape = RoundedCornerShape(8.dp),
- )
- .clickable { onClick() }
- .then(modifier),
- shape = RoundedCornerShape(8.dp),
- color = getSurfaceColor(isLight),
- elevation = elevation?.elevation(true, interactionSource)?.value ?: 0.dp,
+ OutlinedButton(
+ onClick = onClick,
+ modifier = modifier,
+ colors = ButtonDefaults.outlinedButtonColors(
+ contentColor = getTextColor(isLight),
+ backgroundColor = getSurfaceColor(isLight),
+ ),
+ border = BorderStroke(
+ width = 1.dp,
+ color = getBorderColor(isLight),
+ ),
+ contentPadding = PaddingValues(all = 0.dp),
) {
Row(
modifier = Modifier
diff --git a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepositoryTest.kt b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepositoryTest.kt
index 78cce14918fa7773fa12d341c8a3a257efa78af0..6ebcf370a6cb466a02dfb04474b9c96443aed32d 100644
--- a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepositoryTest.kt
+++ b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/data/AuthorizationRepositoryTest.kt
@@ -9,6 +9,7 @@ import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
import assertk.all
import assertk.assertThat
import assertk.assertions.isEqualTo
+import assertk.assertions.isNotEmpty
import assertk.assertions.isNotNull
import assertk.assertions.isNull
import assertk.assertions.prop
@@ -72,8 +73,10 @@ class AuthorizationRepositoryTest {
prop(AuthorizationRequest::responseType).isEqualTo(ResponseTypeValues.CODE)
prop(AuthorizationRequest::redirectUri).isEqualTo(oAuthConfiguration.redirectUri.toUri())
prop(AuthorizationRequest::scope).isEqualTo("scope scope2")
- prop(AuthorizationRequest::codeVerifier).isNull()
prop(AuthorizationRequest::loginHint).isEqualTo(emailAddress)
+ prop(AuthorizationRequest::codeVerifier).isNotNull()
+ prop(AuthorizationRequest::codeVerifierChallengeMethod).isEqualTo("S256")
+ prop(AuthorizationRequest::codeVerifierChallenge).isNotNull().isNotEmpty()
}
}
diff --git a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/FakeAuthorizationRepository.kt b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/FakeAuthorizationRepository.kt
index 7c5d4c0f98bc8dc13738bfe9174adf96bfd78885..ceb16b4e5343228080bc80b1c8e693f65c698617 100644
--- a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/FakeAuthorizationRepository.kt
+++ b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/FakeAuthorizationRepository.kt
@@ -12,7 +12,7 @@ class FakeAuthorizationRepository(
private val answerGetAuthorizationResponse: AuthorizationResponse? = null,
private val answerGetAuthorizationException: AuthorizationException? = null,
private val answerGetExchangeToken: AuthorizationResult = AuthorizationResult.Canceled,
-) : DomainContract.AuthorizationRepository {
+) : AccountOAuthDomainContract.AuthorizationRepository {
var recordedGetAuthorizationRequestIntentConfiguration: OAuthConfiguration? = null
var recordedGetAuthorizationRequestIntentEmailAddress: String? = null
diff --git a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignInTest.kt b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignInTest.kt
index 5a23e751704e18910edf6c7e1c7d2edf952fcf2b..e64e134026e6e6702ed1bc3b36bebea018b73194 100644
--- a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignInTest.kt
+++ b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/domain/usecase/FinishOAuthSignInTest.kt
@@ -1,9 +1,9 @@
package app.k9mail.feature.account.oauth.domain.usecase
import android.content.Intent
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.oauth.domain.FakeAuthorizationRepository
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.test.runTest
diff --git a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewKtTest.kt b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewKtTest.kt
index 948dea338111ae208867752700dc44e692e7720b..f393511c2ab815c49b63e05f407529b02a52941f 100644
--- a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewKtTest.kt
+++ b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewKtTest.kt
@@ -3,7 +3,7 @@ package app.k9mail.feature.account.oauth.ui
import app.k9mail.core.ui.compose.testing.ComposeTest
import app.k9mail.core.ui.compose.testing.setContent
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.oauth.domain.entity.OAuthResult
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Effect
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.State
diff --git a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt
index 5b02d150fba618d7c2247960d7ca41dda440ca71..a7769480b0d46cc60dcd20ae876d544b36db372f 100644
--- a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt
+++ b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt
@@ -4,9 +4,9 @@ import android.app.Activity
import android.content.Intent
import app.cash.turbine.testIn
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationIntentResult
import app.k9mail.feature.account.oauth.domain.entity.AuthorizationResult
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Effect
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Error
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract.Event
diff --git a/feature/account/server/certificate/build.gradle.kts b/feature/account/server/certificate/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..a392d2994314fdd4903a1955ef95cecfac6e2b51
--- /dev/null
+++ b/feature/account/server/certificate/build.gradle.kts
@@ -0,0 +1,18 @@
+plugins {
+ id(ThunderbirdPlugins.Library.androidCompose)
+}
+
+android {
+ namespace = "app.k9mail.feature.account.server.certificate"
+ resourcePrefix = "account_server_certificate_"
+}
+
+dependencies {
+ implementation(projects.core.ui.compose.designsystem)
+ implementation(projects.core.common)
+ implementation(projects.feature.account.common)
+
+ implementation(projects.mail.common)
+
+ testImplementation(projects.core.ui.compose.testing)
+}
diff --git a/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ServerCertificateModule.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ServerCertificateModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8f006c12787f2d55fd77246315a86773192e0c98
--- /dev/null
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ServerCertificateModule.kt
@@ -0,0 +1,29 @@
+package app.k9mail.feature.account.server.certificate
+
+import app.k9mail.feature.account.server.certificate.data.InMemoryServerCertificateErrorRepository
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.certificate.domain.usecase.AddServerCertificateException
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorViewModel
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.module.Module
+import org.koin.dsl.module
+
+val featureAccountServerCertificateModule: Module = module {
+
+ single {
+ InMemoryServerCertificateErrorRepository()
+ }
+
+ factory {
+ AddServerCertificateException(
+ localKeyStore = get(),
+ )
+ }
+
+ viewModel {
+ ServerCertificateErrorViewModel(
+ certificateErrorRepository = get(),
+ addServerCertificateException = get(),
+ )
+ }
+}
diff --git a/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/data/InMemoryServerCertificateErrorRepository.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/data/InMemoryServerCertificateErrorRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f07bd32283df68ab7d7190d6693425aa3219f077
--- /dev/null
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/data/InMemoryServerCertificateErrorRepository.kt
@@ -0,0 +1,21 @@
+package app.k9mail.feature.account.server.certificate.data
+
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateError
+
+class InMemoryServerCertificateErrorRepository(
+ private var serverCertificateError: ServerCertificateError? = null,
+) : ServerCertificateDomainContract.ServerCertificateErrorRepository {
+
+ override fun getCertificateError(): ServerCertificateError? {
+ return serverCertificateError
+ }
+
+ override fun setCertificateError(serverCertificateError: ServerCertificateError) {
+ this.serverCertificateError = serverCertificateError
+ }
+
+ override fun clearCertificateError() {
+ serverCertificateError = null
+ }
+}
diff --git a/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/ServerCertificateDomainContract.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/ServerCertificateDomainContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..66700cc34b9058c403f3ff1d0111d38780e4df25
--- /dev/null
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/ServerCertificateDomainContract.kt
@@ -0,0 +1,21 @@
+package app.k9mail.feature.account.server.certificate.domain
+
+import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateError
+import java.security.cert.X509Certificate
+
+interface ServerCertificateDomainContract {
+
+ interface ServerCertificateErrorRepository {
+ fun getCertificateError(): ServerCertificateError?
+
+ fun setCertificateError(serverCertificateError: ServerCertificateError)
+
+ fun clearCertificateError()
+ }
+
+ interface UseCase {
+ fun interface AddServerCertificateException {
+ suspend fun addCertificate(hostname: String, port: Int, certificate: X509Certificate?)
+ }
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/CertificateError.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/entity/ServerCertificateError.kt
similarity index 57%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/CertificateError.kt
rename to feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/entity/ServerCertificateError.kt
index d85b7a0052089cb8718ae9de8be5192c1de78b4e..5a558ad590ee4930ec05c00021ef4b7088181126 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/CertificateError.kt
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/entity/ServerCertificateError.kt
@@ -1,8 +1,8 @@
-package app.k9mail.feature.account.setup.domain.entity
+package app.k9mail.feature.account.server.certificate.domain.entity
import java.security.cert.X509Certificate
-data class CertificateError(
+data class ServerCertificateError(
val hostname: String,
val port: Int,
val certificateChain: List,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/AddServerCertificateException.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/usecase/AddServerCertificateException.kt
similarity index 74%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/AddServerCertificateException.kt
rename to feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/usecase/AddServerCertificateException.kt
index a1cfb8cf2902c94fb34acec0d0c76ed7b9fd214e..de65461cc53e545cfc069982458c0c1280241cc4 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/AddServerCertificateException.kt
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/domain/usecase/AddServerCertificateException.kt
@@ -1,6 +1,6 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.certificate.domain.usecase
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract.UseCase
import com.fsck.k9.mail.ssl.LocalKeyStore
import java.security.cert.X509Certificate
import kotlinx.coroutines.CoroutineDispatcher
@@ -10,7 +10,7 @@ import kotlinx.coroutines.withContext
internal class AddServerCertificateException(
private val localKeyStore: LocalKeyStore,
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
-) : DomainContract.UseCase.AddServerCertificateException {
+) : UseCase.AddServerCertificateException {
override suspend fun addCertificate(hostname: String, port: Int, certificate: X509Certificate?) {
withContext(coroutineDispatcher) {
localKeyStore.addCertificate(hostname, port, certificate)
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorContract.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorContract.kt
similarity index 82%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorContract.kt
rename to feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorContract.kt
index 18ffe42a7f662f33e0b6616a5fb2fba8e585c1d5..60278650b52ef303dc90ae9e01480b63009a1636 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorContract.kt
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorContract.kt
@@ -1,8 +1,8 @@
-package app.k9mail.feature.account.setup.ui.servercertificate
+package app.k9mail.feature.account.server.certificate.ui
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
-interface CertificateErrorContract {
+interface ServerCertificateErrorContract {
interface ViewModel : UnidirectionalViewModel
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorScreen.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorScreen.kt
similarity index 86%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorScreen.kt
rename to feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorScreen.kt
index f02b471a3b99434ab41948d82bb7713765012b0d..bcf6bc6336fd77de8ec1d973c5f36004434c23bb 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorScreen.kt
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorScreen.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.servercertificate
+package app.k9mail.feature.account.server.certificate.ui
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
@@ -21,11 +21,11 @@ import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
-import app.k9mail.feature.account.setup.data.InMemoryCertificateErrorRepository
-import app.k9mail.feature.account.setup.domain.entity.CertificateError
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract.Effect
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract.Event
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract.ViewModel
+import app.k9mail.feature.account.server.certificate.data.InMemoryServerCertificateErrorRepository
+import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateError
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract.Effect
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract.Event
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract.ViewModel
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import org.koin.androidx.compose.koinViewModel
@@ -33,11 +33,11 @@ import org.koin.androidx.compose.koinViewModel
// Note: This is a placeholder with mostly hardcoded text.
// TODO: Replace with final design.
@Composable
-internal fun CertificateErrorScreen(
+fun ServerCertificateErrorScreen(
onCertificateAccepted: () -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier,
- viewModel: ViewModel = koinViewModel(),
+ viewModel: ViewModel = koinViewModel(),
) {
val (state, dispatch) = viewModel.observe { effect ->
when (effect) {
@@ -102,7 +102,7 @@ internal fun CertificateErrorScreen(
@Composable
@DevicePreviews
-internal fun CertificateErrorScreenK9Preview() {
+internal fun ServerCertificateErrorScreenK9Preview() {
val inputStream = """
-----BEGIN CERTIFICATE-----
MIIE8jCCA9qgAwIBAgISA3bsPKY1eoe/RiBO2t8fUvh1MA0GCSqGSIb3DQEBCwUA
@@ -138,19 +138,19 @@ internal fun CertificateErrorScreenK9Preview() {
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificate = certificateFactory.generateCertificate(inputStream) as X509Certificate
- val certificateError = CertificateError(
+ val serverCertificateError = ServerCertificateError(
hostname = "mail.domain.example",
port = 143,
certificateChain = listOf(certificate),
)
K9Theme {
- CertificateErrorScreen(
+ ServerCertificateErrorScreen(
onCertificateAccepted = {},
onBack = {},
- viewModel = CertificateErrorViewModel(
+ viewModel = ServerCertificateErrorViewModel(
addServerCertificateException = { _, _, _ -> },
- certificateErrorRepository = InMemoryCertificateErrorRepository(certificateError),
+ certificateErrorRepository = InMemoryServerCertificateErrorRepository(serverCertificateError),
),
)
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorViewModel.kt b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorViewModel.kt
similarity index 68%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorViewModel.kt
rename to feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorViewModel.kt
index 1edcde33221397e0a63c91f0ea70fa048f57b1d7..15184b6033cd5b4d5f04beac84c8d7938a97c94f 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/servercertificate/CertificateErrorViewModel.kt
+++ b/feature/account/server/certificate/src/main/kotlin/app/k9mail/feature/account/server/certificate/ui/ServerCertificateErrorViewModel.kt
@@ -1,13 +1,13 @@
-package app.k9mail.feature.account.setup.ui.servercertificate
+package app.k9mail.feature.account.server.certificate.ui
import androidx.lifecycle.viewModelScope
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.setup.domain.DomainContract.CertificateErrorRepository
-import app.k9mail.feature.account.setup.domain.DomainContract.UseCase.AddServerCertificateException
-import app.k9mail.feature.account.setup.domain.entity.CertificateError
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract.Effect
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract.Event
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract.State
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract.UseCase
+import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateError
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract.Effect
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract.Event
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract.State
import com.fsck.k9.logging.Timber
import com.fsck.k9.mail.filter.Hex
import java.security.MessageDigest
@@ -15,15 +15,15 @@ import java.security.NoSuchAlgorithmException
import java.security.cert.CertificateEncodingException
import kotlinx.coroutines.launch
-class CertificateErrorViewModel(
- private val certificateErrorRepository: CertificateErrorRepository,
- private val addServerCertificateException: AddServerCertificateException,
+class ServerCertificateErrorViewModel(
+ private val certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
+ private val addServerCertificateException: UseCase.AddServerCertificateException,
initialState: State = State(),
-) : BaseViewModel(initialState), CertificateErrorContract.ViewModel {
- private val certificateError: CertificateError? = certificateErrorRepository.getCertificateError()
+) : BaseViewModel(initialState), ServerCertificateErrorContract.ViewModel {
+ private val serverCertificateError: ServerCertificateError? = certificateErrorRepository.getCertificateError()
init {
- setErrorMessage(buildErrorMessage(certificateError))
+ setErrorMessage(buildErrorMessage(serverCertificateError))
}
override fun event(event: Event) {
@@ -34,7 +34,7 @@ class CertificateErrorViewModel(
}
private fun acceptCertificate() {
- val certificateError = requireNotNull(certificateError)
+ val certificateError = requireNotNull(serverCertificateError)
viewModelScope.launch {
addServerCertificateException.addCertificate(
@@ -56,8 +56,8 @@ class CertificateErrorViewModel(
emitEffect(Effect.NavigateCertificateAccepted)
}
- private fun buildErrorMessage(certificateError: CertificateError?): String {
- val certificate = certificateError?.certificateChain?.firstOrNull() ?: return ""
+ private fun buildErrorMessage(serverCertificateError: ServerCertificateError?): String {
+ val certificate = serverCertificateError?.certificateChain?.firstOrNull() ?: return ""
return buildString {
certificate.subjectAlternativeNames?.let { subjectAlternativeNames ->
diff --git a/feature/account/server/certificate/src/test/kotlin/app/k9mail/feature/account/server/certificate/ServerCertificateModuleKtTest.kt b/feature/account/server/certificate/src/test/kotlin/app/k9mail/feature/account/server/certificate/ServerCertificateModuleKtTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0247bd8b3e8dd3632f50381b3c35dedf244f345c
--- /dev/null
+++ b/feature/account/server/certificate/src/test/kotlin/app/k9mail/feature/account/server/certificate/ServerCertificateModuleKtTest.kt
@@ -0,0 +1,35 @@
+package app.k9mail.feature.account.server.certificate
+
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract
+import com.fsck.k9.mail.ssl.LocalKeyStore
+import org.junit.Test
+import org.koin.core.annotation.KoinExperimentalAPI
+import org.koin.core.module.Module
+import org.koin.dsl.koinApplication
+import org.koin.dsl.module
+import org.koin.test.KoinTest
+import org.koin.test.check.checkModules
+import org.koin.test.verify.verify
+import org.mockito.Mockito
+
+class ServerCertificateModuleKtTest : KoinTest {
+
+ private val externalModule: Module = module {
+ single { Mockito.mock() }
+ }
+
+ @OptIn(KoinExperimentalAPI::class)
+ @Test
+ fun `should have a valid di module`() {
+ featureAccountServerCertificateModule.verify(
+ extraTypes = listOf(
+ ServerCertificateErrorContract.State::class,
+ ),
+ )
+
+ koinApplication {
+ modules(externalModule, featureAccountServerCertificateModule)
+ checkModules()
+ }
+ }
+}
diff --git a/feature/account/server/settings/build.gradle.kts b/feature/account/server/settings/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..5e9c127b1874a05768d2cefb616a339ab037f2a5
--- /dev/null
+++ b/feature/account/server/settings/build.gradle.kts
@@ -0,0 +1,29 @@
+plugins {
+ id(ThunderbirdPlugins.Library.androidCompose)
+}
+
+android {
+ namespace = "app.k9mail.feature.account.server.settings"
+ resourcePrefix = "account_server_settings_"
+
+ buildTypes {
+ debug {
+ manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
+ }
+ release {
+ manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
+ }
+ }
+}
+
+dependencies {
+ implementation(projects.core.ui.compose.designsystem)
+ implementation(projects.core.common)
+
+ implementation(projects.mail.common)
+ implementation(projects.mail.protocols.imap)
+
+ implementation(projects.feature.account.common)
+
+ testImplementation(projects.core.ui.compose.testing)
+}
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ServerSettingsModule.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ServerSettingsModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..79002e6b6b1b860d5350a15677687faf15018421
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ServerSettingsModule.kt
@@ -0,0 +1,30 @@
+package app.k9mail.feature.account.server.settings
+
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsValidator
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsViewModel
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsValidator
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsViewModel
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.module.Module
+import org.koin.dsl.module
+
+val featureAccountServerSettingsModule: Module = module {
+ factory { IncomingServerSettingsValidator() }
+ factory { OutgoingServerSettingsValidator() }
+
+ viewModel {
+ IncomingServerSettingsViewModel(
+ validator = get(),
+ accountStateRepository = get(),
+ )
+ }
+
+ viewModel {
+ OutgoingServerSettingsViewModel(
+ validator = get(),
+ accountStateRepository = get(),
+ )
+ }
+}
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/ServerSettingsDomainContract.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/ServerSettingsDomainContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7a9343b97943609ed682c739bfc629a92c7fed39
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/ServerSettingsDomainContract.kt
@@ -0,0 +1,29 @@
+package app.k9mail.feature.account.server.settings.domain
+
+import app.k9mail.core.common.domain.usecase.validation.ValidationResult
+
+interface ServerSettingsDomainContract {
+
+ interface UseCase {
+
+ fun interface ValidatePassword {
+ fun execute(password: String): ValidationResult
+ }
+
+ fun interface ValidateServer {
+ fun execute(server: String): ValidationResult
+ }
+
+ fun interface ValidatePort {
+ fun execute(port: Long?): ValidationResult
+ }
+
+ fun interface ValidateUsername {
+ fun execute(username: String): ValidationResult
+ }
+
+ fun interface ValidateImapPrefix {
+ fun execute(imapPrefix: String): ValidationResult
+ }
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateImapPrefix.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateImapPrefix.kt
similarity index 72%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateImapPrefix.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateImapPrefix.kt
index 486d0725833dced58b35b7fd13e610fa358c8c87..0866803b46bfcdaf4ffa3ef5de00a86ba3429ce1 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateImapPrefix.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateImapPrefix.kt
@@ -1,10 +1,10 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase
-internal class ValidateImapPrefix : DomainContract.UseCase.ValidateImapPrefix {
+internal class ValidateImapPrefix : UseCase.ValidateImapPrefix {
override fun execute(imapPrefix: String): ValidationResult {
return when {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePassword.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePassword.kt
similarity index 74%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePassword.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePassword.kt
index f7c25483226e98ce55c7c0c164960a63187a0d46..04cb48414b7cb87c8001180d1da26f9420f04064 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePassword.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePassword.kt
@@ -1,10 +1,10 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase
-internal class ValidatePassword : DomainContract.UseCase.ValidatePassword {
+class ValidatePassword : UseCase.ValidatePassword {
// TODO change behavior to allow empty password when no password is required based on auth type
override fun execute(password: String): ValidationResult {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePort.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePort.kt
similarity index 77%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePort.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePort.kt
index f0e31b67b8b5ac070ad4b2b4a8e1e4709bc83b36..4c5f7a5507dc18eeb80807e6de9bb85888cbdb81 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePort.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePort.kt
@@ -1,10 +1,10 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase
-internal class ValidatePort : DomainContract.UseCase.ValidatePort {
+internal class ValidatePort : UseCase.ValidatePort {
override fun execute(port: Long?): ValidationResult {
return when (port) {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServer.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateServer.kt
similarity index 71%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServer.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateServer.kt
index 31fcfb63df72a1faa0c100e59d4455ad1cce0f49..0a4e6a122a2b57daf4c5be4e0be9911e5c7044ae 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServer.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateServer.kt
@@ -1,10 +1,10 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase
-internal class ValidateServer : DomainContract.UseCase.ValidateServer {
+internal class ValidateServer : UseCase.ValidateServer {
// TODO validate domain, ip4 or ip6
override fun execute(server: String): ValidationResult {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateUsername.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateUsername.kt
similarity index 70%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateUsername.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateUsername.kt
index 7399330bb76613eec4a06a38a676f91e0c686afd..34ea8dfad121332f1493490e36ae80685b411f36 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateUsername.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateUsername.kt
@@ -1,10 +1,10 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase
-internal class ValidateUsername : DomainContract.UseCase.ValidateUsername {
+internal class ValidateUsername : UseCase.ValidateUsername {
override fun execute(username: String): ValidationResult {
return when {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/clientcertificate/ClientCertificateInput.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/ClientCertificateInput.kt
similarity index 85%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/clientcertificate/ClientCertificateInput.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/ClientCertificateInput.kt
index 694b0421bd72bfd43c05d5bd28985294c0340650..b35095fc7adb7976d4e111db73009f028a4a0891 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/clientcertificate/ClientCertificateInput.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/ClientCertificateInput.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.clientcertificate
+package app.k9mail.feature.account.server.settings.ui.common
import android.security.KeyChain
import androidx.compose.foundation.layout.Column
@@ -11,7 +11,7 @@ import androidx.compose.ui.res.stringResource
import app.k9mail.core.ui.compose.common.activity.LocalActivity
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlinedFakeSelect
import app.k9mail.core.ui.compose.designsystem.molecule.input.inputContentPadding
-import app.k9mail.feature.account.setup.R
+import app.k9mail.feature.account.server.settings.R
@Composable
fun ClientCertificateInput(
@@ -29,7 +29,7 @@ fun ClientCertificateInput(
) {
val activity = LocalActivity.current
TextFieldOutlinedFakeSelect(
- text = alias ?: stringResource(R.string.account_setup_client_certificate_none_selected),
+ text = alias ?: stringResource(R.string.account_server_settings_client_certificate_none_selected),
onClick = {
KeyChain.choosePrivateKeyAlias(activity, onValueChange, null, null, null, -1, alias)
},
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/AuthenticationTypeStringMapper.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/AuthenticationTypeStringMapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a66246c62277231e378db69d4e0b0b79dad0cd3b
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/AuthenticationTypeStringMapper.kt
@@ -0,0 +1,29 @@
+package app.k9mail.feature.account.server.settings.ui.common.mapper
+
+import android.content.res.Resources
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.server.settings.R
+
+internal fun AuthenticationType.toResourceString(resources: Resources): String {
+ return when (this) {
+ AuthenticationType.None -> {
+ resources.getString(R.string.account_server_settings_authentication_none)
+ }
+
+ AuthenticationType.PasswordCleartext -> {
+ resources.getString(R.string.account_server_settings_authentication_password_cleartext)
+ }
+
+ AuthenticationType.PasswordEncrypted -> {
+ resources.getString(R.string.account_server_settings_authentication_password_encrypted)
+ }
+
+ AuthenticationType.ClientCertificate -> {
+ resources.getString(R.string.account_server_settings_authentication_client_certificate)
+ }
+
+ AuthenticationType.OAuth2 -> {
+ resources.getString(R.string.account_server_settings_authentication_client_oauth)
+ }
+ }
+}
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/ConnectionSecurityStringMapper.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/ConnectionSecurityStringMapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7d27104f20d414197b92624eadd662da740d9532
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/ConnectionSecurityStringMapper.kt
@@ -0,0 +1,15 @@
+package app.k9mail.feature.account.server.settings.ui.common.mapper
+
+import android.content.res.Resources
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.server.settings.R
+
+internal fun ConnectionSecurity.toResourceString(resources: Resources): String {
+ return when (this) {
+ ConnectionSecurity.None -> resources.getString(R.string.account_server_settings_connection_security_none)
+ ConnectionSecurity.StartTLS -> resources.getString(
+ R.string.account_server_settings_connection_security_start_tls,
+ )
+ ConnectionSecurity.TLS -> resources.getString(R.string.account_server_settings_connection_security_ssl)
+ }
+}
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/ValidationErrorStringMapper.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/ValidationErrorStringMapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d196a4de1eb7edfea79628331c59dc6d80197675
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/common/mapper/ValidationErrorStringMapper.kt
@@ -0,0 +1,65 @@
+package app.k9mail.feature.account.server.settings.ui.common.mapper
+
+import android.content.res.Resources
+import app.k9mail.core.common.domain.usecase.validation.ValidationError
+import app.k9mail.feature.account.server.settings.R
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateImapPrefix.ValidateImapPrefixError
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePassword.ValidatePasswordError
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePort.ValidatePortError
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateServer.ValidateServerError
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateUsername.ValidateUsernameError
+
+fun ValidationError.toResourceString(resources: Resources): String {
+ return when (this) {
+ is ValidateServerError -> toServerErrorString(resources)
+ is ValidatePortError -> toPortErrorString(resources)
+ is ValidateUsernameError -> toUsernameErrorString(resources)
+ is ValidatePasswordError -> toPasswordErrorString(resources)
+ is ValidateImapPrefixError -> toImapPrefixErrorString(resources)
+ else -> throw IllegalArgumentException("Unknown error: $this")
+ }
+}
+
+private fun ValidateServerError.toServerErrorString(resources: Resources): String {
+ return when (this) {
+ is ValidateServerError.EmptyServer -> resources.getString(
+ R.string.account_server_settings_validation_error_server_required,
+ )
+ }
+}
+
+private fun ValidatePortError.toPortErrorString(resources: Resources): String {
+ return when (this) {
+ is ValidatePortError.EmptyPort -> resources.getString(
+ R.string.account_server_settings_validation_error_port_required,
+ )
+
+ is ValidatePortError.InvalidPort -> resources.getString(
+ R.string.account_server_settings_validation_error_port_invalid,
+ )
+ }
+}
+
+private fun ValidateUsernameError.toUsernameErrorString(resources: Resources): String {
+ return when (this) {
+ ValidateUsernameError.EmptyUsername -> resources.getString(
+ R.string.account_server_settings_validation_error_username_required,
+ )
+ }
+}
+
+private fun ValidatePasswordError.toPasswordErrorString(resources: Resources): String {
+ return when (this) {
+ ValidatePasswordError.EmptyPassword -> resources.getString(
+ R.string.account_server_settings_validation_error_password_required,
+ )
+ }
+}
+
+private fun ValidateImapPrefixError.toImapPrefixErrorString(resources: Resources): String {
+ return when (this) {
+ ValidateImapPrefixError.BlankImapPrefix -> resources.getString(
+ R.string.account_server_settings_validation_error_imap_prefix_blank,
+ )
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigContent.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsContent.kt
similarity index 84%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigContent.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsContent.kt
index 620c230a6cd56cab119f5cb1b649a4d7e2bc0ec0..447e6beb4046cb87a62d645a2cd258befcc2a5b6 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigContent.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsContent.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
@@ -25,19 +25,18 @@ import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
import app.k9mail.feature.account.common.ui.item.defaultItemPadding
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.ui.clientcertificate.ClientCertificateInput
-import app.k9mail.feature.account.setup.ui.common.mapper.toResourceString
-import app.k9mail.feature.account.setup.ui.common.toResourceString
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
+import app.k9mail.feature.account.server.settings.R
+import app.k9mail.feature.account.server.settings.ui.common.ClientCertificateInput
+import app.k9mail.feature.account.server.settings.ui.common.mapper.toResourceString
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
@Suppress("LongMethod")
@Composable
-internal fun AccountIncomingConfigContent(
+internal fun IncomingServerSettingsContent(
state: State,
onEvent: (Event) -> Unit,
contentPadding: PaddingValues,
@@ -47,7 +46,7 @@ internal fun AccountIncomingConfigContent(
ResponsiveWidthContainer(
modifier = Modifier
- .testTag("AccountIncomingConfigContent")
+ .testTag("IncomingServerSettingsContent")
.padding(contentPadding)
.fillMaxWidth()
.then(modifier),
@@ -68,7 +67,7 @@ internal fun AccountIncomingConfigContent(
options = IncomingProtocolType.all(),
selectedOption = state.protocolType,
onOptionChange = { onEvent(Event.ProtocolTypeChanged(it)) },
- label = stringResource(id = R.string.account_setup_incoming_config_protocol_type_label),
+ label = stringResource(id = R.string.account_server_settings_protocol_type_label),
contentPadding = defaultItemPadding(),
)
}
@@ -78,7 +77,7 @@ internal fun AccountIncomingConfigContent(
text = state.server.value,
errorMessage = state.server.error?.toResourceString(resources),
onTextChange = { onEvent(Event.ServerChanged(it)) },
- label = stringResource(id = R.string.account_setup_incoming_config_server_label),
+ label = stringResource(id = R.string.account_server_settings_server_label),
contentPadding = defaultItemPadding(),
)
}
@@ -89,7 +88,7 @@ internal fun AccountIncomingConfigContent(
optionToStringTransformation = { it.toResourceString(resources) },
selectedOption = state.security,
onOptionChange = { onEvent(Event.SecurityChanged(it)) },
- label = stringResource(id = R.string.account_setup_incoming_config_security_label),
+ label = stringResource(id = R.string.account_server_settings_security_label),
contentPadding = defaultItemPadding(),
)
}
@@ -99,7 +98,7 @@ internal fun AccountIncomingConfigContent(
value = state.port.value,
errorMessage = state.port.error?.toResourceString(resources),
onValueChange = { onEvent(Event.PortChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_port_label),
+ label = stringResource(id = R.string.account_server_settings_port_label),
contentPadding = defaultItemPadding(),
)
}
@@ -110,7 +109,7 @@ internal fun AccountIncomingConfigContent(
optionToStringTransformation = { it.toResourceString(resources) },
selectedOption = state.authenticationType,
onOptionChange = { onEvent(Event.AuthenticationTypeChanged(it)) },
- label = stringResource(id = R.string.account_setup_incoming_config_authentication_label),
+ label = stringResource(id = R.string.account_server_settings_authentication_label),
contentPadding = defaultItemPadding(),
)
}
@@ -120,7 +119,7 @@ internal fun AccountIncomingConfigContent(
text = state.username.value,
errorMessage = state.username.error?.toResourceString(resources),
onTextChange = { onEvent(Event.UsernameChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_username_label),
+ label = stringResource(id = R.string.account_server_settings_username_label),
contentPadding = defaultItemPadding(),
)
}
@@ -140,7 +139,7 @@ internal fun AccountIncomingConfigContent(
ClientCertificateInput(
alias = state.clientCertificateAlias,
onValueChange = { onEvent(Event.ClientCertificateChanged(it)) },
- label = stringResource(id = R.string.account_setup_client_certificate_label),
+ label = stringResource(id = R.string.account_server_settings_client_certificate_label),
contentPadding = defaultItemPadding(),
)
}
@@ -148,7 +147,7 @@ internal fun AccountIncomingConfigContent(
if (state.protocolType == IncomingProtocolType.IMAP) {
item {
CheckboxInput(
- text = stringResource(id = R.string.account_setup_incoming_config_imap_namespace_label),
+ text = stringResource(id = R.string.account_server_settings_incoming_imap_namespace_label),
checked = state.imapAutodetectNamespaceEnabled,
onCheckedChange = { onEvent(Event.ImapAutoDetectNamespaceChanged(it)) },
contentPadding = defaultItemPadding(),
@@ -159,7 +158,7 @@ internal fun AccountIncomingConfigContent(
if (state.imapAutodetectNamespaceEnabled) {
TextInput(
onTextChange = {},
- label = stringResource(id = R.string.account_setup_incoming_config_imap_prefix_label),
+ label = stringResource(id = R.string.account_server_settings_incoming_imap_prefix_label),
contentPadding = defaultItemPadding(),
isEnabled = false,
)
@@ -168,7 +167,7 @@ internal fun AccountIncomingConfigContent(
text = state.imapPrefix.value,
errorMessage = state.imapPrefix.error?.toResourceString(resources),
onTextChange = { onEvent(Event.ImapPrefixChanged(it)) },
- label = stringResource(id = R.string.account_setup_incoming_config_imap_prefix_label),
+ label = stringResource(id = R.string.account_server_settings_incoming_imap_prefix_label),
contentPadding = defaultItemPadding(),
)
}
@@ -176,7 +175,7 @@ internal fun AccountIncomingConfigContent(
item {
CheckboxInput(
- text = stringResource(id = R.string.account_setup_incoming_config_imap_compression_label),
+ text = stringResource(id = R.string.account_server_settings_incoming_imap_compression_label),
checked = state.imapUseCompression,
onCheckedChange = { onEvent(Event.ImapUseCompressionChanged(it)) },
contentPadding = defaultItemPadding(),
@@ -185,7 +184,7 @@ internal fun AccountIncomingConfigContent(
item {
CheckboxInput(
- text = stringResource(R.string.account_setup_incoming_config_imap_send_client_id_label),
+ text = stringResource(R.string.account_server_settings_incoming_imap_send_client_id_label),
checked = state.imapSendClientId,
onCheckedChange = { onEvent(Event.ImapSendClientIdChanged(it)) },
contentPadding = defaultItemPadding(),
@@ -198,9 +197,9 @@ internal fun AccountIncomingConfigContent(
@Composable
@DevicePreviews
-internal fun AccountIncomingConfigContentK9Preview() {
+internal fun IncomingServerSettingsContentK9Preview() {
K9Theme {
- AccountIncomingConfigContent(
+ IncomingServerSettingsContent(
onEvent = { },
state = State(),
contentPadding = PaddingValues(),
@@ -210,9 +209,9 @@ internal fun AccountIncomingConfigContentK9Preview() {
@Composable
@DevicePreviews
-internal fun AccountIncomingConfigContentThunderbirdPreview() {
+internal fun IncomingServerSettingsContentThunderbirdPreview() {
ThunderbirdTheme {
- AccountIncomingConfigContent(
+ IncomingServerSettingsContent(
onEvent = { },
state = State(),
contentPadding = PaddingValues(),
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigContract.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsContract.kt
similarity index 81%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigContract.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsContract.kt
index 403671bb96cae65cad3ccf90c28230c7b8bf678b..07e203f1e768f182a0f16720ca4771ac0d59ffd2 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigContract.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsContract.kt
@@ -1,15 +1,15 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.domain.entity.toDefaultPort
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.entity.toDefaultPort
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
-interface AccountIncomingConfigContract {
+interface IncomingServerSettingsContract {
interface ViewModel : UnidirectionalViewModel
@@ -44,7 +44,7 @@ interface AccountIncomingConfigContract {
data class ImapUseCompressionChanged(val useCompression: Boolean) : Event
data class ImapSendClientIdChanged(val sendClientId: Boolean) : Event
- object LoadAccountSetupState : Event
+ object LoadAccountState : Event
object OnNextClicked : Event
object OnBackClicked : Event
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigScreen.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsScreen.kt
similarity index 51%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigScreen.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsScreen.kt
index 236acee88ba152f9cf48c1f73cc7d040f25c9eac..9340103d7d1289dcf9aef3db14de995de065d19b 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigScreen.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsScreen.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
@@ -10,17 +10,17 @@ import app.k9mail.core.ui.compose.common.mvi.observe
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.common.ui.AccountTopAppBar
import app.k9mail.feature.account.common.ui.WizardNavigationBar
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.ui.common.AccountSetupTopAppBar
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.ViewModel
-import app.k9mail.feature.account.setup.ui.preview.PreviewAccountSetupStateRepository
+import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository
+import app.k9mail.feature.account.server.settings.R
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.ViewModel
@Composable
-internal fun AccountIncomingConfigScreen(
- onNext: (AccountIncomingConfigContract.State) -> Unit,
+fun IncomingServerSettingsScreen(
+ onNext: (IncomingServerSettingsContract.State) -> Unit,
onBack: () -> Unit,
viewModel: ViewModel,
modifier: Modifier = Modifier,
@@ -33,7 +33,7 @@ internal fun AccountIncomingConfigScreen(
}
LaunchedEffect(key1 = Unit) {
- dispatch(Event.LoadAccountSetupState)
+ dispatch(Event.LoadAccountState)
}
BackHandler {
@@ -42,21 +42,19 @@ internal fun AccountIncomingConfigScreen(
Scaffold(
topBar = {
- AccountSetupTopAppBar(
- title = stringResource(id = R.string.account_setup_incoming_config_top_bar_title),
+ AccountTopAppBar(
+ title = stringResource(id = R.string.account_server_settings_incoming_top_bar_title),
)
},
bottomBar = {
WizardNavigationBar(
- nextButtonText = stringResource(id = R.string.account_setup_button_next),
- backButtonText = stringResource(id = R.string.account_setup_button_back),
onNextClick = { dispatch(Event.OnNextClicked) },
onBackClick = { dispatch(Event.OnBackClicked) },
)
},
modifier = modifier,
) { innerPadding ->
- AccountIncomingConfigContent(
+ IncomingServerSettingsContent(
onEvent = { dispatch(it) },
state = state.value,
contentPadding = innerPadding,
@@ -66,14 +64,14 @@ internal fun AccountIncomingConfigScreen(
@Composable
@DevicePreviews
-internal fun AccountIncomingConfigScreenK9Preview() {
+internal fun IncomingServerSettingsScreenK9Preview() {
K9Theme {
- AccountIncomingConfigScreen(
+ IncomingServerSettingsScreen(
onNext = {},
onBack = {},
- viewModel = AccountIncomingConfigViewModel(
- validator = AccountIncomingConfigValidator(),
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ viewModel = IncomingServerSettingsViewModel(
+ validator = IncomingServerSettingsValidator(),
+ accountStateRepository = PreviewAccountStateRepository(),
),
)
}
@@ -81,14 +79,14 @@ internal fun AccountIncomingConfigScreenK9Preview() {
@Composable
@DevicePreviews
-internal fun AccountIncomingConfigScreenThunderbirdPreview() {
+internal fun IncomingServerSettingsScreenThunderbirdPreview() {
ThunderbirdTheme {
- AccountIncomingConfigScreen(
+ IncomingServerSettingsScreen(
onNext = {},
onBack = {},
- viewModel = AccountIncomingConfigViewModel(
- validator = AccountIncomingConfigValidator(),
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ viewModel = IncomingServerSettingsViewModel(
+ validator = IncomingServerSettingsValidator(),
+ accountStateRepository = PreviewAccountStateRepository(),
),
)
}
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateExtensions.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8b326681022d98145adabfefc1d47397a1c4bb04
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateExtensions.kt
@@ -0,0 +1,32 @@
+package app.k9mail.feature.account.server.settings.ui.incoming
+
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.toImmutableList
+
+internal val IncomingServerSettingsContract.State.isPasswordFieldVisible: Boolean
+ get() = authenticationType.isPasswordRequired
+
+internal val IncomingServerSettingsContract.State.allowedAuthenticationTypes: ImmutableList
+ get() = protocolType.allowedAuthenticationTypes.toImmutableList()
+
+internal val IncomingProtocolType.allowedAuthenticationTypes: List
+ get() = when (this) {
+ IncomingProtocolType.IMAP -> {
+ listOf(
+ AuthenticationType.PasswordCleartext,
+ AuthenticationType.PasswordEncrypted,
+ AuthenticationType.ClientCertificate,
+ AuthenticationType.OAuth2,
+ )
+ }
+
+ IncomingProtocolType.POP3 -> {
+ listOf(
+ AuthenticationType.PasswordCleartext,
+ AuthenticationType.PasswordEncrypted,
+ AuthenticationType.ClientCertificate,
+ )
+ }
+ }
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateMapper.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateMapper.kt
similarity index 64%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateMapper.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateMapper.kt
index cfba40c1214aabf1ca269a762474fcd3a5e22ddf..2fef2862ae5541ead6ac57e0ad8540b34348df4c 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateMapper.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateMapper.kt
@@ -1,19 +1,18 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.domain.entity.toAuthType
-import app.k9mail.feature.account.setup.domain.entity.toAuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toMailConnectionSecurity
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.entity.toAuthType
+import app.k9mail.feature.account.common.domain.entity.toAuthenticationType
+import app.k9mail.feature.account.common.domain.entity.toConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toMailConnectionSecurity
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.store.imap.ImapStoreSettings
-internal fun AccountSetupState.toIncomingConfigState(): State {
+fun AccountState.toIncomingConfigState(): State {
val incomingServerSettings = incomingServerSettings
return if (incomingServerSettings == null) {
State(
@@ -58,10 +57,3 @@ private fun State.createExtras(): Map {
emptyMap()
}
}
-
-internal fun State.toValidationState(): AccountValidationContract.State {
- return AccountValidationContract.State(
- serverSettings = toServerSettings(),
- // TODO add authorization state
- )
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigValidator.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsValidator.kt
similarity index 62%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigValidator.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsValidator.kt
index 662e605db2a0a9da7c610a3bdd5c9dba49ae3b67..8f4bc935f58ce140fad40134b717932f8da43cc3 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigValidator.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsValidator.kt
@@ -1,20 +1,20 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
-import app.k9mail.feature.account.setup.domain.usecase.ValidateImapPrefix
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePort
-import app.k9mail.feature.account.setup.domain.usecase.ValidateServer
-import app.k9mail.feature.account.setup.domain.usecase.ValidateUsername
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateImapPrefix
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePassword
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePort
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateServer
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateUsername
-internal class AccountIncomingConfigValidator(
+internal class IncomingServerSettingsValidator(
private val serverValidator: UseCase.ValidateServer = ValidateServer(),
private val portValidator: UseCase.ValidatePort = ValidatePort(),
private val usernameValidator: UseCase.ValidateUsername = ValidateUsername(),
private val passwordValidator: UseCase.ValidatePassword = ValidatePassword(),
private val imapPrefixValidator: UseCase.ValidateImapPrefix = ValidateImapPrefix(),
-) : AccountIncomingConfigContract.Validator {
+) : IncomingServerSettingsContract.Validator {
override fun validateServer(server: String): ValidationResult {
return serverValidator.execute(server)
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigViewModel.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModel.kt
similarity index 75%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigViewModel.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModel.kt
index bb8e09625884152e31413cf5d2dfa3cc0ef949b9..22f814cd97c9dca5de41d2ac0d56f8c434692c81 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigViewModel.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModel.kt
@@ -1,30 +1,27 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.domain.entity.toDefaultPort
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Validator
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.ViewModel
-
-internal class AccountIncomingConfigViewModel(
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.entity.toDefaultPort
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Validator
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.ViewModel
+
+open class IncomingServerSettingsViewModel(
private val validator: Validator,
- private val accountSetupStateRepository: DomainContract.AccountSetupStateRepository,
- initialState: State? = null,
-) : BaseViewModel(
- initialState = initialState ?: accountSetupStateRepository.getState().toIncomingConfigState(),
-),
- ViewModel {
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
+ initialState: State = State(),
+) : BaseViewModel(initialState = initialState), ViewModel {
@Suppress("CyclomaticComplexMethod")
override fun event(event: Event) {
when (event) {
- Event.LoadAccountSetupState -> loadAccountSetupState()
+ Event.LoadAccountState -> loadAccountState()
is Event.ProtocolTypeChanged -> updateProtocolType(event.protocolType)
is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) }
@@ -53,9 +50,9 @@ internal class AccountIncomingConfigViewModel(
}
}
- private fun loadAccountSetupState() {
+ protected open fun loadAccountState() {
updateState {
- accountSetupStateRepository.getState().toIncomingConfigState()
+ accountStateRepository.getState().toIncomingConfigState()
}
}
@@ -117,7 +114,7 @@ internal class AccountIncomingConfigViewModel(
}
if (!hasError) {
- accountSetupStateRepository.saveIncomingServerSettings(state.value.toServerSettings())
+ accountStateRepository.setIncomingServerSettings(state.value.toServerSettings())
navigateNext()
}
}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/FakeAccountIncomingConfigValidator.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/fake/FakeIncomingServerSettingsValidator.kt
similarity index 77%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/FakeAccountIncomingConfigValidator.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/fake/FakeIncomingServerSettingsValidator.kt
index 9835314489ba7e8fef0ac8ba8135be09e8503714..c8762b21392cb19b8ba64291de020cd6f37de627 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/FakeAccountIncomingConfigValidator.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/fake/FakeIncomingServerSettingsValidator.kt
@@ -1,14 +1,15 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming.fake
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
-class FakeAccountIncomingConfigValidator(
+class FakeIncomingServerSettingsValidator(
private val serverAnswer: ValidationResult = ValidationResult.Success,
private val portAnswer: ValidationResult = ValidationResult.Success,
private val usernameAnswer: ValidationResult = ValidationResult.Success,
private val passwordAnswer: ValidationResult = ValidationResult.Success,
private val imapPrefixAnswer: ValidationResult = ValidationResult.Success,
-) : AccountIncomingConfigContract.Validator {
+) : IncomingServerSettingsContract.Validator {
override fun validateServer(server: String): ValidationResult = serverAnswer
override fun validatePort(port: Long?): ValidationResult = portAnswer
override fun validateUsername(username: String): ValidationResult = usernameAnswer
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/fake/FakeIncomingServerSettingsViewModel.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/fake/FakeIncomingServerSettingsViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dec4c3a759478123d369f9bc539cb011ce4099a0
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/fake/FakeIncomingServerSettingsViewModel.kt
@@ -0,0 +1,22 @@
+package app.k9mail.feature.account.server.settings.ui.incoming.fake
+
+import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.ViewModel
+
+class FakeIncomingServerSettingsViewModel(
+ initialState: State = State(),
+) : BaseViewModel(initialState), ViewModel {
+
+ val events = mutableListOf()
+
+ override fun event(event: Event) {
+ events.add(event)
+ }
+
+ fun effect(effect: Effect) {
+ emitEffect(effect)
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigContent.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsContent.kt
similarity index 83%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigContent.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsContent.kt
index ea9bd3bcbcbbf78959c701dd7470e097098d12fd..61a07cc54a696acb6d8459f831ef0f5742bfff6a 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigContent.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsContent.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
@@ -24,19 +24,18 @@ import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
import app.k9mail.feature.account.common.ui.item.defaultItemPadding
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.ui.clientcertificate.ClientCertificateInput
-import app.k9mail.feature.account.setup.ui.common.mapper.toResourceString
-import app.k9mail.feature.account.setup.ui.common.toResourceString
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
+import app.k9mail.feature.account.server.settings.R
+import app.k9mail.feature.account.server.settings.ui.common.ClientCertificateInput
+import app.k9mail.feature.account.server.settings.ui.common.mapper.toResourceString
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
@Suppress("LongMethod")
@Composable
-internal fun AccountOutgoingConfigContent(
+internal fun OutgoingServerSettingsContent(
state: State,
onEvent: (Event) -> Unit,
contentPadding: PaddingValues,
@@ -46,7 +45,7 @@ internal fun AccountOutgoingConfigContent(
ResponsiveWidthContainer(
modifier = Modifier
- .testTag("AccountOutgoingConfigContent")
+ .testTag("OutgoingServerSettingsContent")
.padding(contentPadding)
.fillMaxWidth()
.then(modifier),
@@ -67,7 +66,7 @@ internal fun AccountOutgoingConfigContent(
text = state.server.value,
errorMessage = state.server.error?.toResourceString(resources),
onTextChange = { onEvent(Event.ServerChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_server_label),
+ label = stringResource(id = R.string.account_server_settings_server_label),
isRequired = true,
contentPadding = defaultItemPadding(),
)
@@ -79,7 +78,7 @@ internal fun AccountOutgoingConfigContent(
optionToStringTransformation = { it.toResourceString(resources) },
selectedOption = state.security,
onOptionChange = { onEvent(Event.SecurityChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_security_label),
+ label = stringResource(id = R.string.account_server_settings_security_label),
contentPadding = defaultItemPadding(),
)
}
@@ -89,7 +88,7 @@ internal fun AccountOutgoingConfigContent(
value = state.port.value,
errorMessage = state.port.error?.toResourceString(resources),
onValueChange = { onEvent(Event.PortChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_port_label),
+ label = stringResource(id = R.string.account_server_settings_port_label),
isRequired = true,
contentPadding = defaultItemPadding(),
)
@@ -101,7 +100,7 @@ internal fun AccountOutgoingConfigContent(
optionToStringTransformation = { it.toResourceString(resources) },
selectedOption = state.authenticationType,
onOptionChange = { onEvent(Event.AuthenticationTypeChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_authentication_label),
+ label = stringResource(id = R.string.account_server_settings_authentication_label),
contentPadding = defaultItemPadding(),
)
}
@@ -112,7 +111,7 @@ internal fun AccountOutgoingConfigContent(
text = state.username.value,
errorMessage = state.username.error?.toResourceString(resources),
onTextChange = { onEvent(Event.UsernameChanged(it)) },
- label = stringResource(id = R.string.account_setup_outgoing_config_username_label),
+ label = stringResource(id = R.string.account_server_settings_username_label),
isRequired = true,
contentPadding = defaultItemPadding(),
)
@@ -135,7 +134,7 @@ internal fun AccountOutgoingConfigContent(
ClientCertificateInput(
alias = state.clientCertificateAlias,
onValueChange = { onEvent(Event.ClientCertificateChanged(it)) },
- label = stringResource(id = R.string.account_setup_client_certificate_label),
+ label = stringResource(id = R.string.account_server_settings_client_certificate_label),
contentPadding = defaultItemPadding(),
)
}
@@ -145,9 +144,9 @@ internal fun AccountOutgoingConfigContent(
@Composable
@DevicePreviews
-internal fun AccountOutgoingConfigContentK9Preview() {
+internal fun OutgoingServerSettingsContentK9Preview() {
K9Theme {
- AccountOutgoingConfigContent(
+ OutgoingServerSettingsContent(
onEvent = { },
state = State(),
contentPadding = PaddingValues(),
@@ -157,9 +156,9 @@ internal fun AccountOutgoingConfigContentK9Preview() {
@Composable
@DevicePreviews
-internal fun AccountOutgoingConfigContentThunderbirdPreview() {
+internal fun OutgoingServerSettingsContentThunderbirdPreview() {
ThunderbirdTheme {
- AccountOutgoingConfigContent(
+ OutgoingServerSettingsContent(
onEvent = { },
state = State(),
contentPadding = PaddingValues(),
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigContract.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsContract.kt
similarity index 77%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigContract.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsContract.kt
index daf9e3e4fa1aed145fd184b5d1bc34a0b3b414ba..27c6dce5f1983face0b880da45b50d073191c952 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigContract.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsContract.kt
@@ -1,14 +1,14 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toSmtpDefaultPort
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toSmtpDefaultPort
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
-interface AccountOutgoingConfigContract {
+interface OutgoingServerSettingsContract {
interface ViewModel : UnidirectionalViewModel
@@ -31,7 +31,7 @@ interface AccountOutgoingConfigContract {
data class PasswordChanged(val password: String) : Event
data class ClientCertificateChanged(val clientCertificateAlias: String?) : Event
- object LoadAccountSetupState : Event
+ object LoadAccountState : Event
object OnNextClicked : Event
object OnBackClicked : Event
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigScreen.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsScreen.kt
similarity index 52%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigScreen.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsScreen.kt
index e491c9916af60efe3c739c8e3b406bde1ca1c67f..e06d230c483e7c76f249fe9d6fcff82ea252fc7d 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigScreen.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsScreen.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
@@ -10,16 +10,16 @@ import app.k9mail.core.ui.compose.common.mvi.observe
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.common.ui.AccountTopAppBar
import app.k9mail.feature.account.common.ui.WizardNavigationBar
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.ui.common.AccountSetupTopAppBar
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.ViewModel
-import app.k9mail.feature.account.setup.ui.preview.PreviewAccountSetupStateRepository
+import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository
+import app.k9mail.feature.account.server.settings.R
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.ViewModel
@Composable
-internal fun AccountOutgoingConfigScreen(
+fun OutgoingServerSettingsScreen(
onNext: () -> Unit,
onBack: () -> Unit,
viewModel: ViewModel,
@@ -33,7 +33,7 @@ internal fun AccountOutgoingConfigScreen(
}
LaunchedEffect(key1 = Unit) {
- dispatch(Event.LoadAccountSetupState)
+ dispatch(Event.LoadAccountState)
}
BackHandler {
@@ -42,21 +42,19 @@ internal fun AccountOutgoingConfigScreen(
Scaffold(
topBar = {
- AccountSetupTopAppBar(
- title = stringResource(id = R.string.account_setup_outgoing_config_top_bar_title),
+ AccountTopAppBar(
+ title = stringResource(id = R.string.account_server_settings_outgoing_top_bar_title),
)
},
bottomBar = {
WizardNavigationBar(
- nextButtonText = stringResource(id = R.string.account_setup_button_next),
- backButtonText = stringResource(id = R.string.account_setup_button_back),
onNextClick = { dispatch(Event.OnNextClicked) },
onBackClick = { dispatch(Event.OnBackClicked) },
)
},
modifier = modifier,
) { innerPadding ->
- AccountOutgoingConfigContent(
+ OutgoingServerSettingsContent(
state = state.value,
onEvent = { dispatch(it) },
contentPadding = innerPadding,
@@ -66,14 +64,14 @@ internal fun AccountOutgoingConfigScreen(
@Composable
@DevicePreviews
-internal fun AccountOutgoingConfigScreenK9Preview() {
+internal fun OutgoingServerSettingsScreenK9Preview() {
K9Theme {
- AccountOutgoingConfigScreen(
+ OutgoingServerSettingsScreen(
onNext = {},
onBack = {},
- viewModel = AccountOutgoingConfigViewModel(
- validator = AccountOutgoingConfigValidator(),
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ viewModel = OutgoingServerSettingsViewModel(
+ validator = OutgoingServerSettingsValidator(),
+ accountStateRepository = PreviewAccountStateRepository(),
),
)
}
@@ -81,14 +79,14 @@ internal fun AccountOutgoingConfigScreenK9Preview() {
@Composable
@DevicePreviews
-internal fun AccountOutgoingConfigScreenThunderbirdPreview() {
+internal fun OutgoingServerSettingsScreenThunderbirdPreview() {
ThunderbirdTheme {
- AccountOutgoingConfigScreen(
+ OutgoingServerSettingsScreen(
onNext = {},
onBack = {},
- viewModel = AccountOutgoingConfigViewModel(
- validator = AccountOutgoingConfigValidator(),
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ viewModel = OutgoingServerSettingsViewModel(
+ validator = OutgoingServerSettingsValidator(),
+ accountStateRepository = PreviewAccountStateRepository(),
),
)
}
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateExtensions.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5179c7c72693c4922ea4473922991426d448f7bf
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateExtensions.kt
@@ -0,0 +1,7 @@
+package app.k9mail.feature.account.server.settings.ui.outgoing
+
+internal val OutgoingServerSettingsContract.State.isUsernameFieldVisible: Boolean
+ get() = authenticationType.isUsernameRequired
+
+internal val OutgoingServerSettingsContract.State.isPasswordFieldVisible: Boolean
+ get() = authenticationType.isPasswordRequired
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateMapper.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt
similarity index 57%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateMapper.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt
index ca636f95e2d9cbb2f3f242915e5c30083adb87c4..8543f559622a3969778f14e98a7bf701a0e88521 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateMapper.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt
@@ -1,17 +1,16 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.toAuthType
-import app.k9mail.feature.account.setup.domain.entity.toAuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toMailConnectionSecurity
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.toAuthType
+import app.k9mail.feature.account.common.domain.entity.toAuthenticationType
+import app.k9mail.feature.account.common.domain.entity.toConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toMailConnectionSecurity
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
import com.fsck.k9.mail.ServerSettings
-internal fun AccountSetupState.toOutgoingConfigState(): State {
+fun AccountState.toOutgoingConfigState(): State {
val outgoingServerSettings = outgoingServerSettings
return if (outgoingServerSettings == null) {
State(
@@ -41,10 +40,3 @@ internal fun State.toServerSettings(): ServerSettings {
clientCertificateAlias = clientCertificateAlias,
)
}
-
-internal fun State.toValidationState(): AccountValidationContract.State {
- return AccountValidationContract.State(
- serverSettings = toServerSettings(),
- // TODO add authorization state
- )
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigValidator.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsValidator.kt
similarity index 63%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigValidator.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsValidator.kt
index a4c71d35e4af58d48b2f2019bf9c37788257eb24..b0bf4ca460786e2a88d9f6b37ff975885936c41a 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigValidator.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsValidator.kt
@@ -1,17 +1,17 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePort
-import app.k9mail.feature.account.setup.domain.usecase.ValidateServer
-import app.k9mail.feature.account.setup.domain.usecase.ValidateUsername
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePassword
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePort
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateServer
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidateUsername
-internal class AccountOutgoingConfigValidator(
+internal class OutgoingServerSettingsValidator(
private val serverValidator: ValidateServer = ValidateServer(),
private val portValidator: ValidatePort = ValidatePort(),
private val usernameValidator: ValidateUsername = ValidateUsername(),
private val passwordValidator: ValidatePassword = ValidatePassword(),
-) : AccountOutgoingConfigContract.Validator {
+) : OutgoingServerSettingsContract.Validator {
override fun validateServer(server: String): ValidationResult {
return serverValidator.execute(server)
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigViewModel.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModel.kt
similarity index 67%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigViewModel.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModel.kt
index 73b761e1b8f14151edff81fd1c5edc8251b3c364..34f37159890cce4801a702af306646332f5cf743 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigViewModel.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModel.kt
@@ -1,28 +1,25 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toSmtpDefaultPort
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Validator
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.ViewModel
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toSmtpDefaultPort
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Validator
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.ViewModel
-internal class AccountOutgoingConfigViewModel(
+open class OutgoingServerSettingsViewModel(
private val validator: Validator,
- private val accountSetupStateRepository: DomainContract.AccountSetupStateRepository,
- initialState: State? = State(),
-) : BaseViewModel(
- initialState = initialState ?: accountSetupStateRepository.getState().toOutgoingConfigState(),
-),
- ViewModel {
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
+ initialState: State = State(),
+) : BaseViewModel(initialState = initialState), ViewModel {
override fun event(event: Event) {
when (event) {
- Event.LoadAccountSetupState -> loadAccountSetupState()
+ Event.LoadAccountState -> loadAccountState()
is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) }
is Event.SecurityChanged -> updateSecurity(event.security)
@@ -39,9 +36,9 @@ internal class AccountOutgoingConfigViewModel(
}
}
- private fun loadAccountSetupState() {
+ protected open fun loadAccountState() {
updateState {
- accountSetupStateRepository.getState().toOutgoingConfigState()
+ accountStateRepository.getState().toOutgoingConfigState()
}
}
@@ -81,7 +78,7 @@ internal class AccountOutgoingConfigViewModel(
}
if (!hasError) {
- accountSetupStateRepository.saveOutgoingServerSettings(state.value.toServerSettings())
+ accountStateRepository.setOutgoingServerSettings(state.value.toServerSettings())
navigateNext()
}
}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/FakeAccountOutgoingConfigValidator.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/fake/FakeOutgoingServerSettingsValidator.kt
similarity index 73%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/FakeAccountOutgoingConfigValidator.kt
rename to feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/fake/FakeOutgoingServerSettingsValidator.kt
index 5c342e99592cbac10b0d96443e83043e5a4a0119..0d9f7a24602d445040973b165bbfb3436e4b0c4a 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/FakeAccountOutgoingConfigValidator.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/fake/FakeOutgoingServerSettingsValidator.kt
@@ -1,13 +1,14 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing.fake
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
-class FakeAccountOutgoingConfigValidator(
+class FakeOutgoingServerSettingsValidator(
private val serverAnswer: ValidationResult = ValidationResult.Success,
private val portAnswer: ValidationResult = ValidationResult.Success,
private val usernameAnswer: ValidationResult = ValidationResult.Success,
private val passwordAnswer: ValidationResult = ValidationResult.Success,
-) : AccountOutgoingConfigContract.Validator {
+) : OutgoingServerSettingsContract.Validator {
override fun validateServer(server: String): ValidationResult = serverAnswer
override fun validatePort(port: Long?): ValidationResult = portAnswer
override fun validateUsername(username: String): ValidationResult = usernameAnswer
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/fake/FakeOutgoingServerSettingsViewModel.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/fake/FakeOutgoingServerSettingsViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..60c3feb127de7ad9e618d30327ba85de0440328d
--- /dev/null
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/fake/FakeOutgoingServerSettingsViewModel.kt
@@ -0,0 +1,22 @@
+package app.k9mail.feature.account.server.settings.ui.outgoing.fake
+
+import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.ViewModel
+
+class FakeOutgoingServerSettingsViewModel(
+ initialState: State = State(),
+) : BaseViewModel(initialState), ViewModel {
+
+ val events = mutableListOf()
+
+ override fun event(event: Event) {
+ events.add(event)
+ }
+
+ fun effect(effect: Effect) {
+ emitEffect(effect)
+ }
+}
diff --git a/feature/account/server/settings/src/main/res/values/strings.xml b/feature/account/server/settings/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7b65001b2b9815cd7ac172cec0f6989fc0309db0
--- /dev/null
+++ b/feature/account/server/settings/src/main/res/values/strings.xml
@@ -0,0 +1,38 @@
+
+
+ Protocol
+ Server
+ Security
+ Port
+ Authentication
+ Username
+
+ None
+ StartTLS
+ SSL/TLS
+
+ None
+ Normal password
+ Encrypted password
+ Client certificate
+ OAuth 2.0
+
+ None
+ Client certificate
+
+ Incoming server settings
+
+ Auto-detect IMAP namespace
+ IMAP path prefix
+ Use compression
+ Send client ID
+
+ Outgoing server settings
+
+ Server name is required.
+ Port is required.
+ Port is invalid (must be 1–65535).
+ Username is required.
+ Password is required.
+ Imap prefix can\'t be blank.
+
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateImapPrefixTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateImapPrefixTest.kt
similarity index 93%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateImapPrefixTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateImapPrefixTest.kt
index 02cef9f345b077007aade2307d705e5edadc3ad6..4c38422c6ccdad02da31495d2ec4528a3c260d1c 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateImapPrefixTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateImapPrefixTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import assertk.assertThat
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePasswordTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePasswordTest.kt
similarity index 94%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePasswordTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePasswordTest.kt
index 5f6b85e10d20fc010377b1cdd53ea101c5de5aa7..d77f9a2579fd7fae0302bc7bd1f16d8f71fbd452 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePasswordTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePasswordTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import assertk.assertThat
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePortTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePortTest.kt
similarity index 90%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePortTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePortTest.kt
index ea3600dcfa27c5c0cc5b08f9922ee8759f0dab67..c58d928896472e8b76cfa5755a7c7a145b844a58 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidatePortTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidatePortTest.kt
@@ -1,7 +1,7 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePort.ValidatePortError
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePort.ValidatePortError
import assertk.assertThat
import assertk.assertions.isInstanceOf
import assertk.assertions.prop
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateServerTest.kt
similarity index 94%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateServerTest.kt
index c0278d4892ea901b08842aba5ad0a8ca81840722..0fbfa2b2fbe0778b614c11eb1b8729c1437b2ea6 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateServerTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import assertk.assertThat
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateUsernameTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateUsernameTest.kt
similarity index 94%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateUsernameTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateUsernameTest.kt
index d9a26c46783680a98f31644d99b138ab2635996b..cca6628d121ad181e5d228e739c19b55610cc470 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateUsernameTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/domain/usecase/ValidateUsernameTest.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.settings.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import assertk.assertThat
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsConfigStateTest.kt
similarity index 57%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsConfigStateTest.kt
index b1bdf26301207b2a36e9cd282f247963b7282df3..a30aacf2618b14086766183182a983cb6b0c2f63 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsConfigStateTest.kt
@@ -1,17 +1,17 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.domain.entity.toImapDefaultPort
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.entity.toImapDefaultPort
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
-class AccountIncomingConfigStateTest {
+class IncomingServerSettingsConfigStateTest {
@Test
fun `should set default values`() {
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigScreenKtTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsScreenKtTest.kt
similarity index 64%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigScreenKtTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsScreenKtTest.kt
index 0de96e850dbaad0a3f55e6372782c31fe85875ba..26591d87a7e1e1d9df63b68b767428afed214d7e 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigScreenKtTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsScreenKtTest.kt
@@ -1,25 +1,26 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.incoming
import app.k9mail.core.ui.compose.testing.ComposeTest
import app.k9mail.core.ui.compose.testing.setContent
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.incoming.fake.FakeIncomingServerSettingsViewModel
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.test.runTest
import org.junit.Test
-class AccountOutgoingConfigScreenKtTest : ComposeTest() {
+class IncomingServerSettingsScreenKtTest : ComposeTest() {
@Test
fun `should delegate navigation effects`() = runTest {
val initialState = State()
- val viewModel = FakeAccountOutgoingConfigViewModel(initialState)
+ val viewModel = FakeIncomingServerSettingsViewModel(initialState)
var onNextCounter = 0
var onBackCounter = 0
setContent {
- AccountOutgoingConfigScreen(
+ IncomingServerSettingsScreen(
onNext = { onNextCounter++ },
onBack = { onBackCounter++ },
viewModel = viewModel,
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateMapperKtTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateMapperKtTest.kt
similarity index 81%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateMapperKtTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateMapperKtTest.kt
index fa6350949668dc7dcdb8015903a9c602f81a88b2..31c48ba61c7cc6e52c9e51135890bc20b608c9f3 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateMapperKtTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsStateMapperKtTest.kt
@@ -1,12 +1,12 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
@@ -14,7 +14,7 @@ import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.store.imap.ImapStoreSettings
import org.junit.Test
-class AccountIncomingConfigStateMapperKtTest {
+class IncomingServerSettingsStateMapperKtTest {
@Test
fun `should map to IMAP server settings`() {
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigViewModelTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModelTest.kt
similarity index 78%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigViewModelTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModelTest.kt
index cd0b35cdabb4d2ca351c8113ae7aa5cb5fea2932..d9ea32523dce2336d47dc092a918f45d0f3a9320 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigViewModelTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModelTest.kt
@@ -1,26 +1,26 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.incoming
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
-import app.k9mail.core.ui.compose.testing.mvi.turbines
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toImapDefaultPort
-import app.k9mail.feature.account.setup.domain.entity.toPop3DefaultPort
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toImapDefaultPort
+import app.k9mail.feature.account.common.domain.entity.toPop3DefaultPort
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.incoming.fake.FakeIncomingServerSettingsValidator
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
@@ -29,14 +29,14 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
-class AccountIncomingConfigViewModelTest {
+class IncomingServerSettingsViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
@Test
- fun `should take initial state from repository when no initial state is provided`() = runTest {
- val accountSetupState = AccountSetupState(
+ fun `should load account setup state when LoadAccountState event is received`() = runTest {
+ val accountState = AccountState(
emailAddress = "test@example.com",
incomingServerSettings = ServerSettings(
"imap",
@@ -50,56 +50,17 @@ class AccountIncomingConfigViewModelTest {
extra = emptyMap(),
),
)
- val testSubject = createTestSubject(
- initialState = null,
- repository = InMemoryAccountSetupStateRepository(accountSetupState),
- )
- val turbines = turbines(testSubject)
-
- assertThatAndMviTurbinesConsumed(
- actual = turbines.awaitStateItem(),
- turbines = turbines,
- ) {
- isEqualTo(
- State(
- protocolType = IncomingProtocolType.IMAP,
- server = StringInputField(value = "imap.example.com"),
- security = ConnectionSecurity.TLS,
- port = NumberInputField(value = 123L),
- authenticationType = AuthenticationType.PasswordCleartext,
- username = StringInputField(value = "username"),
- password = StringInputField(value = "password"),
- ),
- )
- }
- }
-
- @Test
- fun `should load account setup state when LoadAccountSetupState event is received`() = runTest {
- val accountSetupState = AccountSetupState(
- emailAddress = "test@example.com",
- incomingServerSettings = ServerSettings(
- "imap",
- "imap.example.com",
- 123,
- MailConnectionSecurity.SSL_TLS_REQUIRED,
- AuthType.PLAIN,
- "username",
- "password",
- clientCertificateAlias = null,
- extra = emptyMap(),
- ),
+ val repository = InMemoryAccountStateRepository(
+ state = AccountState(),
)
- val repository = InMemoryAccountSetupStateRepository(AccountSetupState())
val testSubject = createTestSubject(
- initialState = null,
repository = repository,
)
val turbines = turbinesWithInitialStateCheck(testSubject, State())
- repository.save(accountSetupState)
+ repository.setState(accountState)
- testSubject.event(Event.LoadAccountSetupState)
+ testSubject.event(Event.LoadAccountState)
assertThatAndMviTurbinesConsumed(
actual = turbines.awaitStateItem(),
@@ -278,7 +239,7 @@ class AccountIncomingConfigViewModelTest {
@Test
fun `should save state emit effect NavigateNext when OnNextClicked is received and input valid`() = runTest {
val initialState = State()
- val repository = InMemoryAccountSetupStateRepository()
+ val repository = InMemoryAccountStateRepository()
val testSubject = createTestSubject(
initialState = initialState,
repository = repository,
@@ -300,7 +261,7 @@ class AccountIncomingConfigViewModelTest {
)
assertThat(repository.getState()).isEqualTo(
- AccountSetupState(
+ AccountState(
incomingServerSettings = ServerSettings(
type = "imap",
host = "",
@@ -334,7 +295,7 @@ class AccountIncomingConfigViewModelTest {
val initialState = State(
authenticationType = AuthenticationType.OAuth2,
)
- val repository = InMemoryAccountSetupStateRepository()
+ val repository = InMemoryAccountStateRepository()
val testSubject = createTestSubject(
initialState = initialState,
repository = repository,
@@ -356,7 +317,7 @@ class AccountIncomingConfigViewModelTest {
)
assertThat(repository.getState()).isEqualTo(
- AccountSetupState(
+ AccountState(
emailAddress = null,
incomingServerSettings = ServerSettings(
type = "imap",
@@ -388,11 +349,11 @@ class AccountIncomingConfigViewModelTest {
@Test
fun `should change state and not emit NavigateNext effect when OnNextClicked event received and input invalid`() =
runTest {
- val testSubject = AccountIncomingConfigViewModel(
- validator = FakeAccountIncomingConfigValidator(
+ val testSubject = IncomingServerSettingsViewModel(
+ validator = FakeIncomingServerSettingsValidator(
serverAnswer = ValidationResult.Failure(TestError),
),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
)
val turbines = turbinesWithInitialStateCheck(testSubject, State())
@@ -433,12 +394,12 @@ class AccountIncomingConfigViewModelTest {
private companion object {
fun createTestSubject(
- initialState: State? = null,
- validator: AccountIncomingConfigContract.Validator = FakeAccountIncomingConfigValidator(),
- repository: DomainContract.AccountSetupStateRepository = InMemoryAccountSetupStateRepository(),
- ) = AccountIncomingConfigViewModel(
+ initialState: State = State(),
+ validator: IncomingServerSettingsContract.Validator = FakeIncomingServerSettingsValidator(),
+ repository: AccountDomainContract.AccountStateRepository = InMemoryAccountStateRepository(),
+ ) = IncomingServerSettingsViewModel(
validator = validator,
- accountSetupStateRepository = repository,
+ accountStateRepository = repository,
initialState = initialState,
)
}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigScreenKtTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsScreenKtTest.kt
similarity index 64%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigScreenKtTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsScreenKtTest.kt
index 4525f2534802243e9f9ae3d5921c71c219bfa4ac..a6237be5054ac203a973718e53e8da9df4bc4bb1 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigScreenKtTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsScreenKtTest.kt
@@ -1,25 +1,26 @@
-package app.k9mail.feature.account.setup.ui.incoming
+package app.k9mail.feature.account.server.settings.ui.outgoing
import app.k9mail.core.ui.compose.testing.ComposeTest
import app.k9mail.core.ui.compose.testing.setContent
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.outgoing.fake.FakeOutgoingServerSettingsViewModel
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.test.runTest
import org.junit.Test
-class AccountIncomingConfigScreenKtTest : ComposeTest() {
+class OutgoingServerSettingsScreenKtTest : ComposeTest() {
@Test
fun `should delegate navigation effects`() = runTest {
val initialState = State()
- val viewModel = FakeAccountIncomingConfigViewModel(initialState)
+ val viewModel = FakeOutgoingServerSettingsViewModel(initialState)
var onNextCounter = 0
var onBackCounter = 0
setContent {
- AccountIncomingConfigScreen(
+ OutgoingServerSettingsScreen(
onNext = { onNextCounter++ },
onBack = { onBackCounter++ },
viewModel = viewModel,
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateMapperKtTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt
similarity index 66%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateMapperKtTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt
index b4d47c10f17351cf073614894cc6cca027c6e2ec..f657f33f4e4c794ad26a405701d9d693b0ce80ed 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateMapperKtTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt
@@ -1,18 +1,18 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import org.junit.Test
-class AccountOutgoingConfigStateMapperKtTest {
+class OutgoingServerSettingsStateMapperKtTest {
@Test
fun `should map to server settings`() {
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateTest.kt
similarity index 54%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateTest.kt
index 840fbe5601029abef0b13bf66b72a685cc2f1731..ade28a967981edd7fa273641011b1cfdf4f719a5 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateTest.kt
@@ -1,16 +1,16 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toSmtpDefaultPort
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toSmtpDefaultPort
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
-class AccountOutgoingConfigStateTest {
+class OutgoingServerSettingsStateTest {
@Test
fun `should set default values`() {
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigViewModelTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModelTest.kt
similarity index 74%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigViewModelTest.kt
rename to feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModelTest.kt
index 1d6c57625bdcb98a68cb0bce915d253bff30426f..988a94e5b26df3dc31779835d012636b775714cb 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigViewModelTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModelTest.kt
@@ -1,24 +1,24 @@
-package app.k9mail.feature.account.setup.ui.outgoing
+package app.k9mail.feature.account.server.settings.ui.outgoing
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
-import app.k9mail.core.ui.compose.testing.mvi.turbines
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toSmtpDefaultPort
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.toSmtpDefaultPort
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Effect
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
+import app.k9mail.feature.account.server.settings.ui.outgoing.fake.FakeOutgoingServerSettingsValidator
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
@@ -27,14 +27,14 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
-class AccountOutgoingConfigViewModelTest {
+class OutgoingServerSettingsViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
@Test
- fun `should take initial state from repository when no initial state is provided`() = runTest {
- val accountSetupState = AccountSetupState(
+ fun `should load account setup state when LoadAccountState event is received`() = runTest {
+ val accountState = AccountState(
emailAddress = "test@example.com",
outgoingServerSettings = ServerSettings(
"smtp",
@@ -48,55 +48,17 @@ class AccountOutgoingConfigViewModelTest {
extra = emptyMap(),
),
)
- val testSubject = createTestSubject(
- initialState = null,
- repository = InMemoryAccountSetupStateRepository(accountSetupState),
- )
- val turbines = turbines(testSubject)
-
- assertThatAndMviTurbinesConsumed(
- actual = turbines.awaitStateItem(),
- turbines = turbines,
- ) {
- isEqualTo(
- State(
- server = StringInputField(value = "smtp.example.com"),
- security = ConnectionSecurity.TLS,
- port = NumberInputField(value = 123L),
- authenticationType = AuthenticationType.PasswordCleartext,
- username = StringInputField(value = "username"),
- password = StringInputField(value = "password"),
- ),
- )
- }
- }
-
- @Test
- fun `should load account setup state when LoadAccountSetupState event is received`() = runTest {
- val accountSetupState = AccountSetupState(
- emailAddress = "test@example.com",
- outgoingServerSettings = ServerSettings(
- "smtp",
- "smtp.example.com",
- 123,
- MailConnectionSecurity.SSL_TLS_REQUIRED,
- AuthType.PLAIN,
- "username",
- "password",
- clientCertificateAlias = null,
- extra = emptyMap(),
- ),
+ val repository = InMemoryAccountStateRepository(
+ state = AccountState(),
)
- val repository = InMemoryAccountSetupStateRepository(AccountSetupState())
val testSubject = createTestSubject(
- initialState = null,
repository = repository,
)
val turbines = turbinesWithInitialStateCheck(testSubject, State())
- repository.save(accountSetupState)
+ repository.setState(accountState)
- testSubject.event(Event.LoadAccountSetupState)
+ testSubject.event(Event.LoadAccountState)
assertThatAndMviTurbinesConsumed(
actual = turbines.awaitStateItem(),
@@ -198,7 +160,7 @@ class AccountOutgoingConfigViewModelTest {
@Test
fun `should save state and emit effect NavigateNext when OnNextClicked is received and input valid`() = runTest {
val initialState = State()
- val repository = InMemoryAccountSetupStateRepository()
+ val repository = InMemoryAccountStateRepository()
val testSubject = createTestSubject(
initialState = initialState,
repository = repository,
@@ -218,7 +180,7 @@ class AccountOutgoingConfigViewModelTest {
)
assertThat(repository.getState()).isEqualTo(
- AccountSetupState(
+ AccountState(
outgoingServerSettings = ServerSettings(
type = "smtp",
host = "",
@@ -247,7 +209,7 @@ class AccountOutgoingConfigViewModelTest {
val initialState = State(
authenticationType = AuthenticationType.OAuth2,
)
- val repository = InMemoryAccountSetupStateRepository()
+ val repository = InMemoryAccountStateRepository()
val testSubject = createTestSubject(
initialState = initialState,
repository = repository,
@@ -267,7 +229,7 @@ class AccountOutgoingConfigViewModelTest {
)
assertThat(repository.getState()).isEqualTo(
- AccountSetupState(
+ AccountState(
outgoingServerSettings = ServerSettings(
type = "smtp",
host = "",
@@ -295,7 +257,7 @@ class AccountOutgoingConfigViewModelTest {
runTest {
val initialState = State()
val testSubject = createTestSubject(
- validator = FakeAccountOutgoingConfigValidator(
+ validator = FakeOutgoingServerSettingsValidator(
serverAnswer = ValidationResult.Failure(TestError),
),
initialState = initialState,
@@ -339,12 +301,12 @@ class AccountOutgoingConfigViewModelTest {
private companion object {
fun createTestSubject(
- initialState: State? = null,
- validator: AccountOutgoingConfigContract.Validator = FakeAccountOutgoingConfigValidator(),
- repository: DomainContract.AccountSetupStateRepository = InMemoryAccountSetupStateRepository(),
- ) = AccountOutgoingConfigViewModel(
+ initialState: State = State(),
+ validator: OutgoingServerSettingsContract.Validator = FakeOutgoingServerSettingsValidator(),
+ repository: AccountDomainContract.AccountStateRepository = InMemoryAccountStateRepository(),
+ ) = OutgoingServerSettingsViewModel(
validator = validator,
- accountSetupStateRepository = repository,
+ accountStateRepository = repository,
initialState = initialState,
)
}
diff --git a/feature/account/server/validation/build.gradle.kts b/feature/account/server/validation/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..75c9d1be0ea6356a4fd50ea84b7f6be3856b224e
--- /dev/null
+++ b/feature/account/server/validation/build.gradle.kts
@@ -0,0 +1,33 @@
+plugins {
+ id(ThunderbirdPlugins.Library.androidCompose)
+}
+
+android {
+ namespace = "app.k9mail.feature.account.server.validation"
+ resourcePrefix = "account_server_validation_"
+
+ buildTypes {
+ debug {
+ manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
+ }
+ release {
+ manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
+ }
+ }
+}
+
+dependencies {
+ implementation(projects.core.ui.compose.designsystem)
+ implementation(projects.core.common)
+
+ implementation(projects.mail.common)
+ implementation(projects.mail.protocols.imap)
+ implementation(projects.mail.protocols.pop3)
+ implementation(projects.mail.protocols.smtp)
+
+ implementation(projects.feature.account.common)
+ implementation(projects.feature.account.oauth)
+ implementation(projects.feature.account.server.certificate)
+
+ testImplementation(projects.core.ui.compose.testing)
+}
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ServerValidationModule.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ServerValidationModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8ebf9661079e4d93dade2ea7951315563ebef641
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ServerValidationModule.kt
@@ -0,0 +1,63 @@
+package app.k9mail.feature.account.server.validation
+
+import app.k9mail.core.common.coreCommonModule
+import app.k9mail.feature.account.common.featureAccountCommonModule
+import app.k9mail.feature.account.oauth.featureAccountOAuthModule
+import app.k9mail.feature.account.server.certificate.featureAccountServerCertificateModule
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
+import app.k9mail.feature.account.server.validation.domain.usecase.ValidateServerSettings
+import app.k9mail.feature.account.server.validation.ui.IncomingServerValidationViewModel
+import app.k9mail.feature.account.server.validation.ui.OutgoingServerValidationViewModel
+import com.fsck.k9.mail.store.imap.ImapServerSettingsValidator
+import com.fsck.k9.mail.store.pop3.Pop3ServerSettingsValidator
+import com.fsck.k9.mail.transport.smtp.SmtpServerSettingsValidator
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.qualifier.named
+import org.koin.dsl.module
+
+val featureAccountServerValidationModule = module {
+ includes(
+ coreCommonModule,
+ featureAccountCommonModule,
+ featureAccountServerCertificateModule,
+ featureAccountOAuthModule,
+ )
+
+ factory {
+ ValidateServerSettings(
+ authStateStorage = get(),
+ imapValidator = ImapServerSettingsValidator(
+ trustedSocketFactory = get(),
+ oAuth2TokenProviderFactory = get(),
+ clientIdAppName = get(named("ClientIdAppName")),
+ ),
+ pop3Validator = Pop3ServerSettingsValidator(
+ trustedSocketFactory = get(),
+ ),
+ smtpValidator = SmtpServerSettingsValidator(
+ trustedSocketFactory = get(),
+ oAuth2TokenProviderFactory = get(),
+ ),
+ )
+ }
+
+ viewModel {
+ IncomingServerValidationViewModel(
+ validateServerSettings = get(),
+ accountStateRepository = get(),
+ authorizationStateRepository = get(),
+ certificateErrorRepository = get(),
+ oAuthViewModel = get(),
+ )
+ }
+
+ viewModel {
+ OutgoingServerValidationViewModel(
+ validateServerSettings = get(),
+ accountStateRepository = get(),
+ authorizationStateRepository = get(),
+ certificateErrorRepository = get(),
+ oAuthViewModel = get(),
+ )
+ }
+}
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/domain/ServerValidationDomainContract.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/domain/ServerValidationDomainContract.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7ee70de5cb3b2f2adf9b245b77826573ca0facae
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/domain/ServerValidationDomainContract.kt
@@ -0,0 +1,14 @@
+package app.k9mail.feature.account.server.validation.domain
+
+import com.fsck.k9.mail.ServerSettings
+import com.fsck.k9.mail.server.ServerSettingsValidationResult
+
+interface ServerValidationDomainContract {
+
+ interface UseCase {
+
+ fun interface ValidateServerSettings {
+ suspend fun execute(settings: ServerSettings): ServerSettingsValidationResult
+ }
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerSettings.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/ValidateServerSettings.kt
similarity index 82%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerSettings.kt
rename to feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/ValidateServerSettings.kt
index 0d5a8f4ccd14be8058555fbc3b58a6faa33d89ee..f96d2ba087416a3e2a8d7c0744655b0311df12f3 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerSettings.kt
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/ValidateServerSettings.kt
@@ -1,6 +1,6 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.validation.domain.usecase
-import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.oauth.AuthStateStorage
import com.fsck.k9.mail.server.ServerSettingsValidationResult
@@ -9,13 +9,13 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-internal class ValidateServerSettings(
+class ValidateServerSettings(
private val authStateStorage: AuthStateStorage,
private val imapValidator: ServerSettingsValidator,
private val pop3Validator: ServerSettingsValidator,
private val smtpValidator: ServerSettingsValidator,
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
-) : UseCase.ValidateServerSettings {
+) : ServerValidationDomainContract.UseCase.ValidateServerSettings {
override suspend fun execute(settings: ServerSettings): ServerSettingsValidationResult {
return withContext(coroutineDispatcher) {
when (settings.type) {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationViewModel.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/BaseServerValidationViewModel.kt
similarity index 75%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationViewModel.kt
rename to feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/BaseServerValidationViewModel.kt
index f628ff65c26abf5a4f6743eb5339033d7c8bf938..57fe861160aea82fc7f4dd3d1b599866a88db008 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationViewModel.kt
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/BaseServerValidationViewModel.kt
@@ -1,41 +1,43 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
import androidx.lifecycle.viewModelScope
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
import app.k9mail.feature.account.oauth.domain.entity.OAuthResult
+import app.k9mail.feature.account.oauth.domain.entity.isOAuth
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.CertificateError
-import app.k9mail.feature.account.setup.domain.entity.isOAuth
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Effect
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Error
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Event
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.State
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.certificate.domain.entity.ServerCertificateError
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Error
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
import com.fsck.k9.mail.server.ServerSettingsValidationResult
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import app.k9mail.feature.account.oauth.domain.DomainContract as OAuthDomainContract
private const val CONTINUE_NEXT_DELAY = 2000L
@Suppress("TooManyFunctions")
-internal class AccountValidationViewModel(
- private val validateServerSettings: DomainContract.UseCase.ValidateServerSettings,
- private val accountSetupStateRepository: DomainContract.AccountSetupStateRepository,
- private val authorizationStateRepository: OAuthDomainContract.AuthorizationStateRepository,
- private val certificateErrorRepository: DomainContract.CertificateErrorRepository,
+abstract class BaseServerValidationViewModel(
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
+ private val validateServerSettings: ServerValidationDomainContract.UseCase.ValidateServerSettings,
+ private val authorizationStateRepository: AccountOAuthDomainContract.AuthorizationStateRepository,
+ private val certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
override val oAuthViewModel: AccountOAuthContract.ViewModel,
override val isIncomingValidation: Boolean = true,
initialState: State? = null,
) : BaseViewModel(
- initialState = initialState ?: accountSetupStateRepository.getState().toValidationState(isIncomingValidation),
+ initialState = initialState ?: accountStateRepository.getState().toServerValidationState(isIncomingValidation),
),
- AccountValidationContract.ViewModel {
+ ServerValidationContract.ViewModel {
override fun event(event: Event) {
when (event) {
- Event.LoadAccountSetupStateAndValidate -> loadAccountSetupStateAndValidate()
+ Event.LoadAccountStateAndValidate -> loadAccountStateAndValidate()
is Event.OnOAuthResult -> onOAuthResult(event.result)
Event.ValidateServerSettings -> onValidateConfig()
Event.OnNextClicked -> navigateNext()
@@ -45,9 +47,9 @@ internal class AccountValidationViewModel(
}
}
- private fun loadAccountSetupStateAndValidate() {
+ private fun loadAccountStateAndValidate() {
updateState {
- accountSetupStateRepository.getState().toValidationState(isIncomingValidation)
+ accountStateRepository.getState().toServerValidationState(isIncomingValidation)
}
onValidateConfig()
}
@@ -63,7 +65,7 @@ internal class AccountValidationViewModel(
}
private fun checkOAuthState() {
- val authorizationState = accountSetupStateRepository.getState().authorizationState
+ val authorizationState = accountStateRepository.getState().authorizationState
if (authorizationState != null) {
if (authorizationStateRepository.isAuthorized(authorizationState)) {
validateServerSettings()
@@ -100,7 +102,7 @@ internal class AccountValidationViewModel(
private fun onOAuthResult(result: OAuthResult) {
if (result is OAuthResult.Success) {
- accountSetupStateRepository.saveAuthorizationState(result.authorizationState)
+ accountStateRepository.setAuthorizationState(result.authorizationState)
updateState {
it.copy(
needsAuthorization = false,
@@ -168,7 +170,7 @@ internal class AccountValidationViewModel(
val serverSettings = checkNotNull(state.value.serverSettings)
certificateErrorRepository.setCertificateError(
- CertificateError(
+ ServerCertificateError(
hostname = serverSettings.host!!,
port = serverSettings.port,
certificateChain = error.certificateChain,
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/IncomingServerValidationViewModel.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/IncomingServerValidationViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dcef4fc7489e352d39f65014c274eb92721e3384
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/IncomingServerValidationViewModel.kt
@@ -0,0 +1,25 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract.UseCase
+
+class IncomingServerValidationViewModel(
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ validateServerSettings: UseCase.ValidateServerSettings,
+ authorizationStateRepository: AccountOAuthDomainContract.AuthorizationStateRepository,
+ certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
+ oAuthViewModel: AccountOAuthContract.ViewModel,
+ initialState: ServerValidationContract.State? = null,
+) : BaseServerValidationViewModel(
+ accountStateRepository = accountStateRepository,
+ validateServerSettings = validateServerSettings,
+ authorizationStateRepository = authorizationStateRepository,
+ certificateErrorRepository = certificateErrorRepository,
+ oAuthViewModel = oAuthViewModel,
+ initialState = initialState,
+ isIncomingValidation = true,
+),
+ ServerValidationContract.IncomingViewModel
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/OutgoingServerValidationViewModel.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/OutgoingServerValidationViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..da1d13db8835236698cc55f9e2393c9f779747e8
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/OutgoingServerValidationViewModel.kt
@@ -0,0 +1,25 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract.UseCase
+
+class OutgoingServerValidationViewModel(
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ validateServerSettings: UseCase.ValidateServerSettings,
+ authorizationStateRepository: AccountOAuthDomainContract.AuthorizationStateRepository,
+ certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
+ oAuthViewModel: AccountOAuthContract.ViewModel,
+ initialState: ServerValidationContract.State? = null,
+) : BaseServerValidationViewModel(
+ accountStateRepository = accountStateRepository,
+ validateServerSettings = validateServerSettings,
+ authorizationStateRepository = authorizationStateRepository,
+ certificateErrorRepository = certificateErrorRepository,
+ oAuthViewModel = oAuthViewModel,
+ initialState = initialState,
+ isIncomingValidation = false,
+),
+ ServerValidationContract.OutgoingViewModel
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationContent.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationContent.kt
similarity index 81%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationContent.kt
rename to feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationContent.kt
index c380abafc1b3723ecfed7a30e170be7d4b7c0766..ba7b942d4de717fc4d0355eae4898a1ed2a5e101 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationContent.kt
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationContent.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -27,13 +27,13 @@ import app.k9mail.feature.account.common.ui.item.SuccessItem
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
import app.k9mail.feature.account.oauth.ui.AccountOAuthView
import app.k9mail.feature.account.oauth.ui.preview.PreviewAccountOAuthViewModel
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Event
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.State
+import app.k9mail.feature.account.server.validation.R
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
@Suppress("LongMethod")
@Composable
-internal fun AccountValidationContent(
+internal fun ServerValidationContent(
state: State,
isIncomingValidation: Boolean,
oAuthViewModel: AccountOAuthContract.ViewModel,
@@ -63,9 +63,9 @@ internal fun AccountValidationContent(
ErrorItem(
title = stringResource(
id = if (isIncomingValidation) {
- R.string.account_setup_settings_validation_incoming_loading_error
+ R.string.account_server_validation_incoming_loading_error
} else {
- R.string.account_setup_settings_validation_outgoing_loading_error
+ R.string.account_server_validation_outgoing_loading_error
},
),
message = state.error.toResourceString(resources),
@@ -77,9 +77,9 @@ internal fun AccountValidationContent(
SuccessItem(
message = stringResource(
id = if (isIncomingValidation) {
- R.string.account_setup_settings_validation_incoming_success
+ R.string.account_server_validation_incoming_success
} else {
- R.string.account_setup_settings_validation_outgoing_success
+ R.string.account_server_validation_outgoing_success
},
),
)
@@ -94,7 +94,7 @@ internal fun AccountValidationContent(
) {
TextSubtitle1(
text = stringResource(
- id = R.string.account_setup_settings_validation_sign_in,
+ id = R.string.account_server_validation_sign_in,
),
)
Spacer(modifier = Modifier.padding(MainTheme.spacings.default))
@@ -110,9 +110,9 @@ internal fun AccountValidationContent(
LoadingItem(
message = stringResource(
id = if (isIncomingValidation) {
- R.string.account_setup_settings_validation_incoming_loading_message
+ R.string.account_server_validation_incoming_loading_message
} else {
- R.string.account_setup_settings_validation_outgoing_loading_message
+ R.string.account_server_validation_outgoing_loading_message
},
),
)
@@ -124,9 +124,9 @@ internal fun AccountValidationContent(
@Composable
@DevicePreviews
-internal fun AccountIncomingValidationContentPreview() {
+internal fun IncomingServerValidationContentPreview() {
PreviewWithThemes {
- AccountValidationContent(
+ ServerValidationContent(
onEvent = { },
state = State(),
isIncomingValidation = true,
@@ -138,9 +138,9 @@ internal fun AccountIncomingValidationContentPreview() {
@Composable
@DevicePreviews
-internal fun AccountOutgoingValidationContentPreview() {
+internal fun OutgoingServerValidationContentPreview() {
PreviewWithThemes {
- AccountValidationContent(
+ ServerValidationContent(
onEvent = { },
state = State(),
isIncomingValidation = false,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationContract.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationContract.kt
similarity index 87%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationContract.kt
rename to feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationContract.kt
index a7edfee0dc05d7a1d29d97256f12c56a175e6c2c..a4555c6f35c83f53a8a0549a9e814edff964f756 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationContract.kt
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationContract.kt
@@ -1,4 +1,4 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
import app.k9mail.feature.account.oauth.domain.entity.OAuthResult
@@ -7,13 +7,16 @@ import com.fsck.k9.mail.ServerSettings
import java.io.IOException
import java.security.cert.X509Certificate
-interface AccountValidationContract {
+interface ServerValidationContract {
interface ViewModel : UnidirectionalViewModel {
val isIncomingValidation: Boolean
val oAuthViewModel: AccountOAuthContract.ViewModel
}
+ interface OutgoingViewModel : ViewModel
+ interface IncomingViewModel : ViewModel
+
data class State(
val emailAddress: String? = null,
val serverSettings: ServerSettings? = null,
@@ -24,7 +27,7 @@ interface AccountValidationContract {
)
sealed interface Event {
- object LoadAccountSetupStateAndValidate : Event
+ object LoadAccountStateAndValidate : Event
data class OnOAuthResult(val result: OAuthResult) : Event
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationMainScreen.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationMainScreen.kt
new file mode 100644
index 0000000000000000000000000000000000000000..419e24a7ea7bc6e6cf487cc68b0f718649dc3d26
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationMainScreen.kt
@@ -0,0 +1,97 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import app.k9mail.core.ui.compose.common.DevicePreviews
+import app.k9mail.core.ui.compose.common.mvi.observeWithoutEffect
+import app.k9mail.core.ui.compose.designsystem.template.Scaffold
+import app.k9mail.core.ui.compose.theme.K9Theme
+import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.common.ui.AppTitleTopHeader
+import app.k9mail.feature.account.common.ui.WizardNavigationBar
+import app.k9mail.feature.account.common.ui.WizardNavigationBarState
+import app.k9mail.feature.account.oauth.ui.preview.PreviewAccountOAuthViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.ViewModel
+import app.k9mail.feature.account.server.validation.ui.fake.FakeIncomingServerValidationViewModel
+import app.k9mail.feature.account.server.validation.ui.fake.FakeOutgoingServerValidationViewModel
+
+@Composable
+internal fun ServerValidationMainScreen(
+ viewModel: ViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val (state, dispatch) = viewModel.observeWithoutEffect()
+
+ Scaffold(
+ topBar = {
+ AppTitleTopHeader()
+ },
+ bottomBar = {
+ WizardNavigationBar(
+ onNextClick = {},
+ onBackClick = { dispatch(Event.OnBackClicked) },
+ state = WizardNavigationBarState(
+ showNext = false,
+ ),
+ )
+ },
+ modifier = modifier,
+ ) { innerPadding ->
+ ServerValidationContent(
+ onEvent = { dispatch(it) },
+ state = state.value,
+ isIncomingValidation = viewModel.isIncomingValidation,
+ oAuthViewModel = viewModel.oAuthViewModel,
+ contentPadding = innerPadding,
+ )
+ }
+}
+
+@Composable
+@DevicePreviews
+internal fun IncomingServerValidationScreenK9Preview() {
+ K9Theme {
+ ServerValidationMainScreen(
+ viewModel = FakeIncomingServerValidationViewModel(
+ oAuthViewModel = PreviewAccountOAuthViewModel(),
+ ),
+ )
+ }
+}
+
+@Composable
+@DevicePreviews
+internal fun IncomingServerValidationScreenThunderbirdPreview() {
+ ThunderbirdTheme {
+ ServerValidationMainScreen(
+ viewModel = FakeIncomingServerValidationViewModel(
+ oAuthViewModel = PreviewAccountOAuthViewModel(),
+ ),
+ )
+ }
+}
+
+@Composable
+@DevicePreviews
+internal fun AccountOutgoingValidationScreenK9Preview() {
+ K9Theme {
+ ServerValidationMainScreen(
+ viewModel = FakeOutgoingServerValidationViewModel(
+ oAuthViewModel = PreviewAccountOAuthViewModel(),
+ ),
+ )
+ }
+}
+
+@Composable
+@DevicePreviews
+internal fun AccountOutgoingValidationScreenThunderbirdPreview() {
+ ThunderbirdTheme {
+ ServerValidationMainScreen(
+ viewModel = FakeOutgoingServerValidationViewModel(
+ oAuthViewModel = PreviewAccountOAuthViewModel(),
+ ),
+ )
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationScreen.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationScreen.kt
similarity index 63%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationScreen.kt
rename to feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationScreen.kt
index 0b7de7ac321d8fc7b6249674c642d7d64ad41285..32f16da0d19cdff20e64d5bcc6c4defd07a716c8 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationScreen.kt
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationScreen.kt
@@ -1,17 +1,17 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.common.mvi.observe
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorScreen
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Effect
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Event
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.ViewModel
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorScreen
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.ViewModel
@Composable
-internal fun AccountValidationScreen(
+fun ServerValidationScreen(
onNext: () -> Unit,
onBack: () -> Unit,
viewModel: ViewModel,
@@ -25,21 +25,21 @@ internal fun AccountValidationScreen(
}
LaunchedEffect(key1 = Unit) {
- dispatch(Event.LoadAccountSetupStateAndValidate)
+ dispatch(Event.LoadAccountStateAndValidate)
}
BackHandler {
dispatch(Event.OnBackClicked)
}
- if (state.value.error is AccountValidationContract.Error.CertificateError) {
- CertificateErrorScreen(
+ if (state.value.error is ServerValidationContract.Error.CertificateError) {
+ ServerCertificateErrorScreen(
onCertificateAccepted = { dispatch(Event.OnCertificateAccepted) },
onBack = { dispatch(Event.OnBackClicked) },
modifier = modifier,
)
} else {
- AccountValidationMainScreen(
+ ServerValidationMainScreen(
viewModel = viewModel,
modifier = modifier,
)
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStateMapper.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStateMapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b140ae26465e3758fce28c22059c81497ef5721f
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStateMapper.kt
@@ -0,0 +1,13 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import app.k9mail.feature.account.common.domain.entity.AccountState
+
+internal fun AccountState.toServerValidationState(isIncomingValidation: Boolean): ServerValidationContract.State {
+ return ServerValidationContract.State(
+ emailAddress = emailAddress,
+ serverSettings = if (isIncomingValidation) incomingServerSettings else outgoingServerSettings,
+ isLoading = false,
+ isSuccess = false,
+ error = null,
+ )
+}
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStringMapper.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStringMapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..228e1d9fa163b66bc373fb15418c04342b533cf9
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStringMapper.kt
@@ -0,0 +1,29 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import android.content.res.Resources
+import app.k9mail.feature.account.server.validation.R
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Error
+
+internal fun Error.toResourceString(resources: Resources): String {
+ return when (this) {
+ is Error.AuthenticationError -> resources.getString(
+ R.string.account_server_validation_error_authentication,
+ )
+
+ is Error.CertificateError -> resources.getString(
+ R.string.account_server_validation_error_certificate,
+ )
+
+ is Error.NetworkError -> resources.getString(
+ R.string.account_server_validation_error_network,
+ )
+
+ is Error.ServerError -> resources.getString(
+ R.string.account_server_validation_error_server,
+ )
+
+ is Error.UnknownError -> resources.getString(
+ R.string.account_server_validation_error_unknown,
+ )
+ }
+}
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeIncomingServerValidationViewModel.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeIncomingServerValidationViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..38b7fbefe8487585c91446e4bd81d48c84f3ae9c
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeIncomingServerValidationViewModel.kt
@@ -0,0 +1,27 @@
+package app.k9mail.feature.account.server.validation.ui.fake
+
+import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.ViewModel
+
+class FakeIncomingServerValidationViewModel(
+ override val oAuthViewModel: AccountOAuthContract.ViewModel = FakeAccountOAuthViewModel(),
+ override val isIncomingValidation: Boolean = true,
+ initialState: State = State(),
+) : BaseViewModel(initialState), ServerValidationContract.IncomingViewModel {
+
+ val events = mutableListOf()
+
+ override fun event(event: Event) {
+ events.add(event)
+ }
+
+ fun effect(effect: Effect) {
+ emitEffect(effect)
+ }
+}
diff --git a/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeOutgoingServerValidationViewModel.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeOutgoingServerValidationViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3b82517c18ed0e855161b92be4d483e52e93410f
--- /dev/null
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeOutgoingServerValidationViewModel.kt
@@ -0,0 +1,27 @@
+package app.k9mail.feature.account.server.validation.ui.fake
+
+import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.ViewModel
+
+class FakeOutgoingServerValidationViewModel(
+ override val oAuthViewModel: AccountOAuthContract.ViewModel = FakeAccountOAuthViewModel(),
+ override val isIncomingValidation: Boolean = true,
+ initialState: State = State(),
+) : BaseViewModel(initialState), ServerValidationContract.OutgoingViewModel {
+
+ val events = mutableListOf()
+
+ override fun event(event: Event) {
+ events.add(event)
+ }
+
+ fun effect(effect: Effect) {
+ emitEffect(effect)
+ }
+}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/FakeAccountValidationViewModel.kt b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeServerValidationViewModel.kt
similarity index 60%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/FakeAccountValidationViewModel.kt
rename to feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeServerValidationViewModel.kt
index 41840998e4a24380cfb1ed25eecab9e08534a650..d0bc2cf010cd40549d9bbbe6580efc5ff4c8f6df 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/FakeAccountValidationViewModel.kt
+++ b/feature/account/server/validation/src/main/kotlin/app/k9mail/feature/account/server/validation/ui/fake/FakeServerValidationViewModel.kt
@@ -1,14 +1,14 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui.fake
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
-import app.k9mail.feature.account.setup.ui.FakeAccountOAuthViewModel
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Effect
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Event
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.State
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.ViewModel
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.ViewModel
-class FakeAccountValidationViewModel(
+class FakeServerValidationViewModel(
override val oAuthViewModel: AccountOAuthContract.ViewModel = FakeAccountOAuthViewModel(),
override val isIncomingValidation: Boolean = true,
initialState: State = State(),
diff --git a/feature/account/server/validation/src/main/res/values/strings.xml b/feature/account/server/validation/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..603ba06d16740cc41ca535a44412b54f9aa39619
--- /dev/null
+++ b/feature/account/server/validation/src/main/res/values/strings.xml
@@ -0,0 +1,15 @@
+
+
+ Authentication error
+ Certificate error
+ Network error
+ Server error
+ Unknown error
+ Checking incoming server settings…
+ Checking incoming server settings failed!
+ Incoming server settings are valid!
+ Checking outgoing server settings…
+ Checking outgoing server settings failed!
+ Outgoing server settings are valid!
+ Please sign in
+
diff --git a/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ServerValidationModuleKtTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ServerValidationModuleKtTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..deff0f85db03dd43dd22ecece16bbc801aae8e51
--- /dev/null
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ServerValidationModuleKtTest.kt
@@ -0,0 +1,74 @@
+package app.k9mail.feature.account.server.validation
+
+import android.content.Context
+import app.k9mail.core.common.oauth.OAuthConfigurationFactory
+import app.k9mail.feature.account.common.AccountCommonExternalContract
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
+import com.fsck.k9.mail.oauth.OAuth2TokenProvider
+import com.fsck.k9.mail.oauth.OAuth2TokenProviderFactory
+import com.fsck.k9.mail.ssl.LocalKeyStore
+import com.fsck.k9.mail.ssl.TrustedSocketFactory
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.annotation.KoinExperimentalAPI
+import org.koin.core.module.Module
+import org.koin.core.qualifier.named
+import org.koin.dsl.koinApplication
+import org.koin.dsl.module
+import org.koin.test.KoinTest
+import org.koin.test.check.checkModules
+import org.koin.test.verify.verify
+import org.mockito.Mockito.mock
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+
+@RunWith(RobolectricTestRunner::class)
+class ServerValidationModuleKtTest : KoinTest {
+
+ private val externalModule: Module = module {
+ single {
+ TrustedSocketFactory { _, _, _, _ -> null }
+ }
+ single { OAuthConfigurationFactory { emptyMap() } }
+ single {
+ OAuth2TokenProviderFactory { _ ->
+ object : OAuth2TokenProvider {
+ override fun getToken(timeoutMillis: Long) = TODO()
+ override fun invalidateToken() = TODO()
+ }
+ }
+ }
+ single { mock() }
+ factory { mock() }
+ single(named("ClientIdAppName")) { "App Name" }
+ }
+
+ @OptIn(KoinExperimentalAPI::class)
+ @Test
+ fun `should have a valid di module`() {
+ featureAccountServerValidationModule.verify(
+ extraTypes = listOf(
+ ServerValidationContract.State::class,
+ AccountDomainContract.AccountStateRepository::class,
+ AccountCommonExternalContract.AccountStateLoader::class,
+ ServerCertificateDomainContract.ServerCertificateErrorRepository::class,
+ ServerCertificateErrorContract.State::class,
+ AccountState::class,
+ Context::class,
+ Boolean::class,
+ Class.forName("net.openid.appauth.AppAuthConfiguration").kotlin,
+ ),
+ )
+
+ koinApplication {
+ modules(externalModule, featureAccountServerValidationModule)
+ androidContext(RuntimeEnvironment.getApplication())
+ checkModules()
+ }
+ }
+}
diff --git a/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/FakeAuthStateStorage.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/FakeAuthStateStorage.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bcfb0d546df4f472d4a275b28ee303e82a17fc2a
--- /dev/null
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/FakeAuthStateStorage.kt
@@ -0,0 +1,17 @@
+package app.k9mail.feature.account.server.validation.domain.usecase
+
+import com.fsck.k9.mail.oauth.AuthStateStorage
+
+class FakeAuthStateStorage(
+ private var authorizationState: String? = null,
+) : AuthStateStorage {
+ override fun getAuthorizationState(): String? = authorizationState
+
+ override fun updateAuthorizationState(authorizationState: String?) {
+ this.authorizationState = authorizationState
+ }
+
+ override fun getEmail(): String? {
+ return null
+ }
+}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerSettingsTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/ValidateServerSettingsTest.kt
similarity index 96%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerSettingsTest.kt
rename to feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/ValidateServerSettingsTest.kt
index 8fcec7ff2f08a9cfaa3f934da1e571e10bff0a23..a1f7c145800806472381908219271646f6c53593 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateServerSettingsTest.kt
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/domain/usecase/ValidateServerSettingsTest.kt
@@ -1,6 +1,5 @@
-package app.k9mail.feature.account.setup.domain.usecase
+package app.k9mail.feature.account.server.validation.domain.usecase
-import app.k9mail.feature.account.setup.ui.validation.InMemoryAuthStateStorage
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
@@ -12,7 +11,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test
class ValidateServerSettingsTest {
- private val authStateStorage = InMemoryAuthStateStorage()
+ private val authStateStorage = FakeAuthStateStorage()
@Test
fun `should check with imap validator when protocol is imap`() = runTest {
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationViewModelTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/BaseServerValidationViewModelTest.kt
similarity index 71%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationViewModelTest.kt
rename to feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/BaseServerValidationViewModelTest.kt
index f64341ded11d688c0a93b8319ace40ece4e18948..af0f8b03d1241174010d2bc3f3c6b5c1911e4ea9 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationViewModelTest.kt
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/BaseServerValidationViewModelTest.kt
@@ -1,16 +1,21 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
-import app.k9mail.feature.account.setup.data.InMemoryCertificateErrorRepository
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.ui.FakeAccountOAuthViewModel
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Effect
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Error
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Event
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.State
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.server.certificate.data.InMemoryServerCertificateErrorRepository
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Error
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Event
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isTrue
@@ -23,16 +28,22 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
-class AccountValidationViewModelTest {
+abstract class BaseServerValidationViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
@Test
- fun `should update state when LoadAccountSetupStateAndValidate event received and validate`() = runTest {
- val accountSetupState = AccountSetupState(
- incomingServerSettings = IMAP_SERVER_SETTINGS,
- )
+ fun `should update state when LoadAccountStateAndValidate event received and validate`() = runTest {
+ val accountState = if (isIncomingValidation) {
+ AccountState(
+ incomingServerSettings = SERVER_SETTINGS,
+ )
+ } else {
+ AccountState(
+ outgoingServerSettings = SERVER_SETTINGS,
+ )
+ }
val initialState = State(
serverSettings = null,
isLoading = true,
@@ -40,20 +51,20 @@ class AccountValidationViewModelTest {
isSuccess = true,
)
val testSubject = createTestSubject(
- accountSetupState = accountSetupState,
+ accountState = accountState,
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(testSubject, initialState)
val expectedState = State(
- serverSettings = IMAP_SERVER_SETTINGS,
+ serverSettings = SERVER_SETTINGS,
isLoading = false,
error = null,
isSuccess = false,
)
- testSubject.event(Event.LoadAccountSetupStateAndValidate)
+ testSubject.event(Event.LoadAccountStateAndValidate)
assertThat(turbines.awaitStateItem()).isEqualTo(expectedState)
@@ -93,7 +104,7 @@ class AccountValidationViewModelTest {
@Test
fun `should validate server settings when ValidateServerSettings event received`() = runTest {
val initialState = State(
- serverSettings = IMAP_SERVER_SETTINGS,
+ serverSettings = SERVER_SETTINGS,
)
val testSubject = createTestSubject(
serverSettingsValidationResult = ServerSettingsValidationResult.Success,
@@ -123,7 +134,7 @@ class AccountValidationViewModelTest {
@Test
fun `should set error state when ValidateServerSettings received and check settings failed`() = runTest {
val initialState = State(
- serverSettings = IMAP_SERVER_SETTINGS,
+ serverSettings = SERVER_SETTINGS,
)
val testSubject = createTestSubject(
serverSettingsValidationResult = ServerSettingsValidationResult.ServerError("server error"),
@@ -151,7 +162,7 @@ class AccountValidationViewModelTest {
@Test
fun `should emit effect NavigateNext when ValidateConfig is successful`() = runTest {
val initialState = State(
- serverSettings = IMAP_SERVER_SETTINGS,
+ serverSettings = SERVER_SETTINGS,
isSuccess = true,
)
val testSubject = createTestSubject(
@@ -187,19 +198,20 @@ class AccountValidationViewModelTest {
@Test
fun `should clear error and trigger check settings when OnRetryClicked event received`() = runTest {
val initialState = State(
- serverSettings = IMAP_SERVER_SETTINGS,
+ serverSettings = SERVER_SETTINGS,
error = Error.ServerError("server error"),
)
var checkSettingsCalled = false
- val testSubject = AccountValidationViewModel(
+
+ val testSubject = createTestSubject(
validateServerSettings = {
delay(50)
checkSettingsCalled = true
ServerSettingsValidationResult.Success
},
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
authorizationStateRepository = { true },
- certificateErrorRepository = InMemoryCertificateErrorRepository(),
+ certificateErrorRepository = InMemoryServerCertificateErrorRepository(),
oAuthViewModel = FakeAccountOAuthViewModel(),
initialState = initialState,
)
@@ -225,26 +237,25 @@ class AccountValidationViewModelTest {
}
}
- private companion object {
- fun createTestSubject(
- serverSettingsValidationResult: ServerSettingsValidationResult = ServerSettingsValidationResult.Success,
- accountSetupState: AccountSetupState = AccountSetupState(),
- initialState: State = State(),
- ): AccountValidationViewModel {
- return AccountValidationViewModel(
- validateServerSettings = {
- delay(50)
- serverSettingsValidationResult
- },
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(accountSetupState),
- authorizationStateRepository = { true },
- certificateErrorRepository = InMemoryCertificateErrorRepository(),
- oAuthViewModel = FakeAccountOAuthViewModel(),
- initialState = initialState,
- )
- }
-
- val IMAP_SERVER_SETTINGS = ServerSettings(
+ abstract fun createTestSubject(
+ serverSettingsValidationResult: ServerSettingsValidationResult = ServerSettingsValidationResult.Success,
+ accountState: AccountState = AccountState(),
+ initialState: State = State(),
+ ): T
+
+ abstract fun createTestSubject(
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ validateServerSettings: ServerValidationDomainContract.UseCase.ValidateServerSettings,
+ authorizationStateRepository: AccountOAuthDomainContract.AuthorizationStateRepository,
+ certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
+ oAuthViewModel: AccountOAuthContract.ViewModel,
+ initialState: State,
+ ): T
+
+ abstract val isIncomingValidation: Boolean
+
+ protected companion object {
+ val SERVER_SETTINGS = ServerSettings(
type = "imap",
host = "imap.example.com",
port = 465,
diff --git a/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/IncomingServerValidationViewModelTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/IncomingServerValidationViewModelTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..82ae20d0e17e336432c00802f1452d61183e2f7e
--- /dev/null
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/IncomingServerValidationViewModelTest.kt
@@ -0,0 +1,77 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.server.certificate.data.InMemoryServerCertificateErrorRepository
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Error
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
+import assertk.assertThat
+import assertk.assertions.isTrue
+import com.fsck.k9.mail.server.ServerSettingsValidationResult
+import kotlinx.coroutines.delay
+import org.junit.Test
+
+class IncomingServerValidationViewModelTest : BaseServerValidationViewModelTest() {
+
+ @Test
+ fun `should set isIncoming to true`() {
+ val testSubject = createTestSubject(
+ serverSettingsValidationResult = ServerSettingsValidationResult.Success,
+ accountState = AccountState(
+ incomingServerSettings = SERVER_SETTINGS,
+ ),
+ initialState = State(
+ serverSettings = null,
+ isLoading = true,
+ error = Error.ServerError("server error"),
+ isSuccess = true,
+ ),
+ )
+
+ assertThat(testSubject.isIncomingValidation).isTrue()
+ }
+
+ override fun createTestSubject(
+ serverSettingsValidationResult: ServerSettingsValidationResult,
+ accountState: AccountState,
+ initialState: State,
+ ): IncomingServerValidationViewModel {
+ return IncomingServerValidationViewModel(
+ validateServerSettings = {
+ delay(50)
+ serverSettingsValidationResult
+ },
+ accountStateRepository = InMemoryAccountStateRepository(
+ state = accountState,
+ ),
+ authorizationStateRepository = { true },
+ certificateErrorRepository = InMemoryServerCertificateErrorRepository(),
+ oAuthViewModel = FakeAccountOAuthViewModel(),
+ initialState = initialState,
+ )
+ }
+
+ override fun createTestSubject(
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ validateServerSettings: ServerValidationDomainContract.UseCase.ValidateServerSettings,
+ authorizationStateRepository: AccountOAuthDomainContract.AuthorizationStateRepository,
+ certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
+ oAuthViewModel: AccountOAuthContract.ViewModel,
+ initialState: State,
+ ) = IncomingServerValidationViewModel(
+ accountStateRepository = accountStateRepository,
+ validateServerSettings = validateServerSettings,
+ authorizationStateRepository = authorizationStateRepository,
+ certificateErrorRepository = certificateErrorRepository,
+ oAuthViewModel = oAuthViewModel,
+ initialState = initialState,
+ )
+
+ override val isIncomingValidation: Boolean = true
+}
diff --git a/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/OutgoingServerValidationViewModelTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/OutgoingServerValidationViewModelTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..408a95ce150589bf840bd510467187f2a27532bb
--- /dev/null
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/OutgoingServerValidationViewModelTest.kt
@@ -0,0 +1,77 @@
+package app.k9mail.feature.account.server.validation.ui
+
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.oauth.domain.AccountOAuthDomainContract
+import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.server.certificate.data.InMemoryServerCertificateErrorRepository
+import app.k9mail.feature.account.server.certificate.domain.ServerCertificateDomainContract
+import app.k9mail.feature.account.server.validation.domain.ServerValidationDomainContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Error
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
+import assertk.assertThat
+import assertk.assertions.isFalse
+import com.fsck.k9.mail.server.ServerSettingsValidationResult
+import kotlinx.coroutines.delay
+import org.junit.Test
+
+class OutgoingServerValidationViewModelTest : BaseServerValidationViewModelTest() {
+
+ @Test
+ fun `should set isIncoming to false`() {
+ val testSubject = createTestSubject(
+ serverSettingsValidationResult = ServerSettingsValidationResult.Success,
+ accountState = AccountState(
+ outgoingServerSettings = SERVER_SETTINGS,
+ ),
+ initialState = State(
+ serverSettings = null,
+ isLoading = true,
+ error = Error.ServerError("server error"),
+ isSuccess = true,
+ ),
+ )
+
+ assertThat(testSubject.isIncomingValidation).isFalse()
+ }
+
+ override fun createTestSubject(
+ serverSettingsValidationResult: ServerSettingsValidationResult,
+ accountState: AccountState,
+ initialState: State,
+ ): OutgoingServerValidationViewModel {
+ return OutgoingServerValidationViewModel(
+ validateServerSettings = {
+ delay(50)
+ serverSettingsValidationResult
+ },
+ accountStateRepository = InMemoryAccountStateRepository(
+ state = accountState,
+ ),
+ authorizationStateRepository = { true },
+ certificateErrorRepository = InMemoryServerCertificateErrorRepository(),
+ oAuthViewModel = FakeAccountOAuthViewModel(),
+ initialState = initialState,
+ )
+ }
+
+ override fun createTestSubject(
+ accountStateRepository: AccountDomainContract.AccountStateRepository,
+ validateServerSettings: ServerValidationDomainContract.UseCase.ValidateServerSettings,
+ authorizationStateRepository: AccountOAuthDomainContract.AuthorizationStateRepository,
+ certificateErrorRepository: ServerCertificateDomainContract.ServerCertificateErrorRepository,
+ oAuthViewModel: AccountOAuthContract.ViewModel,
+ initialState: State,
+ ) = OutgoingServerValidationViewModel(
+ accountStateRepository = accountStateRepository,
+ validateServerSettings = validateServerSettings,
+ authorizationStateRepository = authorizationStateRepository,
+ certificateErrorRepository = certificateErrorRepository,
+ oAuthViewModel = oAuthViewModel,
+ initialState = initialState,
+ )
+
+ override val isIncomingValidation: Boolean = false
+}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationScreenKtTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationScreenKtTest.kt
similarity index 71%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationScreenKtTest.kt
rename to feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationScreenKtTest.kt
index eb3710914d9c80e1cfba0adbf0f84bea5df373d1..a4828a420e6585dd9cfa970cc5f6640c81e3aacd 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationScreenKtTest.kt
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationScreenKtTest.kt
@@ -1,27 +1,28 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
import app.k9mail.core.ui.compose.testing.ComposeTest
import app.k9mail.core.ui.compose.testing.setContent
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Effect
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.State
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.Effect
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
+import app.k9mail.feature.account.server.validation.ui.fake.FakeServerValidationViewModel
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.test.runTest
import org.junit.Test
-class AccountValidationScreenKtTest : ComposeTest() {
+class ServerValidationScreenKtTest : ComposeTest() {
@Test
fun `should delegate navigation effects`() = runTest {
val initialState = State()
- val viewModel = FakeAccountValidationViewModel(initialState = initialState)
+ val viewModel = FakeServerValidationViewModel(initialState = initialState)
var onNextCounter = 0
var onBackCounter = 0
setContent {
ThunderbirdTheme {
- AccountValidationScreen(
+ ServerValidationScreen(
onNext = { onNextCounter++ },
onBack = { onBackCounter++ },
viewModel = viewModel,
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStateTest.kt b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStateTest.kt
similarity index 72%
rename from feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStateTest.kt
rename to feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStateTest.kt
index ca29ffb3d74510d531a685f481218d08590329b6..c7d7c4bd9cd33e8c09b0ff0705a24638b0b7a9a3 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStateTest.kt
+++ b/feature/account/server/validation/src/test/kotlin/app/k9mail/feature/account/server/validation/ui/ServerValidationStateTest.kt
@@ -1,11 +1,11 @@
-package app.k9mail.feature.account.setup.ui.validation
+package app.k9mail.feature.account.server.validation.ui
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.State
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
-class AccountValidationStateTest {
+class ServerValidationStateTest {
@Test
fun `should set default values`() {
diff --git a/feature/account/setup/build.gradle.kts b/feature/account/setup/build.gradle.kts
index fbe8659767747b1ba60995071a512956da7a30ab..29072b8e41c7e74b5e6e50c02652780a13b67710 100644
--- a/feature/account/setup/build.gradle.kts
+++ b/feature/account/setup/build.gradle.kts
@@ -19,14 +19,19 @@ android {
dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.common)
+
implementation(projects.mail.common)
implementation(projects.mail.protocols.imap)
implementation(projects.mail.protocols.pop3)
implementation(projects.mail.protocols.smtp)
implementation(projects.feature.autodiscovery.service)
- implementation(projects.feature.account.common)
+
+ api(projects.feature.account.common)
implementation(projects.feature.account.oauth)
+ implementation(projects.feature.account.server.settings)
+ implementation(projects.feature.account.server.certificate)
+ api(projects.feature.account.server.validation)
testImplementation(projects.core.ui.compose.testing)
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupExternalContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupExternalContract.kt
index 768153f0685fa1433e72e79cc38a32fb665c6636..554f4180fece89d73522be4c3451c3c38af87fe3 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupExternalContract.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupExternalContract.kt
@@ -1,6 +1,6 @@
package app.k9mail.feature.account.setup
-import app.k9mail.feature.account.setup.domain.entity.Account
+import app.k9mail.feature.account.common.domain.entity.Account
interface AccountSetupExternalContract {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt
index 0a1de6b030f6c4b403765170bbfc3f811f140ff9..6659bf885f4273083e27859280cff62f21c07cbe 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt
@@ -2,43 +2,32 @@ package app.k9mail.feature.account.setup
import app.k9mail.autodiscovery.api.AutoDiscoveryService
import app.k9mail.autodiscovery.service.RealAutoDiscoveryService
-import app.k9mail.core.common.coreCommonModule
+import app.k9mail.feature.account.common.featureAccountCommonModule
import app.k9mail.feature.account.oauth.featureAccountOAuthModule
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
-import app.k9mail.feature.account.setup.data.InMemoryCertificateErrorRepository
+import app.k9mail.feature.account.server.settings.featureAccountServerSettingsModule
+import app.k9mail.feature.account.server.validation.featureAccountServerValidationModule
import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.usecase.AddServerCertificateException
import app.k9mail.feature.account.setup.domain.usecase.CreateAccount
import app.k9mail.feature.account.setup.domain.usecase.GetAutoDiscovery
-import app.k9mail.feature.account.setup.domain.usecase.ValidateServerSettings
import app.k9mail.feature.account.setup.ui.AccountSetupViewModel
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryValidator
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryViewModel
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigValidator
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigViewModel
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract
import app.k9mail.feature.account.setup.ui.options.AccountOptionsValidator
import app.k9mail.feature.account.setup.ui.options.AccountOptionsViewModel
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigValidator
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigViewModel
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorViewModel
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationViewModel
-import com.fsck.k9.mail.oauth.AuthStateStorage
-import com.fsck.k9.mail.store.imap.ImapServerSettingsValidator
-import com.fsck.k9.mail.store.pop3.Pop3ServerSettingsValidator
-import com.fsck.k9.mail.transport.smtp.SmtpServerSettingsValidator
import okhttp3.OkHttpClient
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.module.Module
-import org.koin.core.qualifier.named
-import org.koin.dsl.binds
import org.koin.dsl.module
val featureAccountSetupModule: Module = module {
- includes(coreCommonModule, featureAccountOAuthModule)
+ includes(
+ featureAccountCommonModule,
+ featureAccountOAuthModule,
+ featureAccountServerValidationModule,
+ featureAccountServerSettingsModule,
+ )
single {
OkHttpClient()
@@ -57,36 +46,6 @@ val featureAccountSetupModule: Module = module {
)
}
- single {
- InMemoryAccountSetupStateRepository()
- }.binds(arrayOf(DomainContract.AccountSetupStateRepository::class, AuthStateStorage::class))
-
- single { InMemoryCertificateErrorRepository() }
-
- factory {
- ValidateServerSettings(
- authStateStorage = get(),
- imapValidator = ImapServerSettingsValidator(
- trustedSocketFactory = get(),
- oAuth2TokenProviderFactory = get(),
- clientIdAppName = "null",
- ),
- pop3Validator = Pop3ServerSettingsValidator(
- trustedSocketFactory = get(),
- ),
- smtpValidator = SmtpServerSettingsValidator(
- trustedSocketFactory = get(),
- oAuth2TokenProviderFactory = get(),
- ),
- )
- }
-
- factory {
- AddServerCertificateException(
- localKeyStore = get(),
- )
- }
-
factory {
CreateAccount(
accountCreator = get(),
@@ -94,69 +53,27 @@ val featureAccountSetupModule: Module = module {
}
factory { AccountAutoDiscoveryValidator() }
- factory { AccountIncomingConfigValidator() }
- factory { AccountOutgoingConfigValidator() }
factory { AccountOptionsValidator() }
viewModel {
AccountSetupViewModel(
createAccount = get(),
- accountSetupStateRepository = get(),
+ accountStateRepository = get(),
)
}
viewModel {
AccountAutoDiscoveryViewModel(
validator = get(),
getAutoDiscovery = get(),
- accountSetupStateRepository = get(),
+ accountStateRepository = get(),
oAuthViewModel = get(),
)
}
- viewModel {
- AccountIncomingConfigViewModel(
- validator = get(),
- accountSetupStateRepository = get(),
- )
- }
- viewModel(named(NAME_INCOMING_VALIDATION)) {
- AccountValidationViewModel(
- validateServerSettings = get(),
- accountSetupStateRepository = get(),
- authorizationStateRepository = get(),
- certificateErrorRepository = get(),
- oAuthViewModel = get(),
- isIncomingValidation = true,
- )
- }
- viewModel {
- AccountOutgoingConfigViewModel(
- validator = get(),
- accountSetupStateRepository = get(),
- )
- }
- viewModel(named(NAME_OUTGOING_VALIDATION)) {
- AccountValidationViewModel(
- validateServerSettings = get(),
- accountSetupStateRepository = get(),
- authorizationStateRepository = get(),
- certificateErrorRepository = get(),
- oAuthViewModel = get(),
- isIncomingValidation = false,
- )
- }
+
viewModel {
AccountOptionsViewModel(
validator = get(),
- accountSetupStateRepository = get(),
- )
- }
- viewModel {
- CertificateErrorViewModel(
- certificateErrorRepository = get(),
- addServerCertificateException = get(),
+ accountStateRepository = get(),
)
}
}
-
-internal const val NAME_INCOMING_VALIDATION = "incoming_validation"
-internal const val NAME_OUTGOING_VALIDATION = "outgoing_validation"
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/data/InMemoryAccountSetupStateRepository.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/data/InMemoryAccountSetupStateRepository.kt
deleted file mode 100644
index fa1cea0b561009bd4fdba90329d226b550f08031..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/data/InMemoryAccountSetupStateRepository.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package app.k9mail.feature.account.setup.data
-
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import com.fsck.k9.mail.ServerSettings
-import com.fsck.k9.mail.oauth.AuthStateStorage
-
-class InMemoryAccountSetupStateRepository(
- private var state: AccountSetupState = AccountSetupState(),
-) : DomainContract.AccountSetupStateRepository, AuthStateStorage {
-
- override fun getState(): AccountSetupState {
- return state
- }
-
- override fun save(accountSetupState: AccountSetupState) {
- state = accountSetupState
- }
-
- override fun saveEmailAddress(emailAddress: String) {
- state = state.copy(emailAddress = emailAddress)
- }
-
- override fun saveIncomingServerSettings(serverSettings: ServerSettings) {
- state = state.copy(incomingServerSettings = serverSettings)
- }
-
- override fun saveOutgoingServerSettings(serverSettings: ServerSettings) {
- state = state.copy(outgoingServerSettings = serverSettings)
- }
-
- override fun saveAuthorizationState(authorizationState: AuthorizationState) {
- state = state.copy(authorizationState = authorizationState)
- }
-
- override fun getEmail(): String? {
- return state.emailAddress
- }
-
- override fun saveOptions(options: AccountOptions) {
- state = state.copy(options = options)
- }
-
- override fun clear() {
- state = AccountSetupState()
- }
-
- override fun getAuthorizationState(): String? {
- return state.authorizationState?.state
- }
-
- override fun updateAuthorizationState(authorizationState: String?) {
- state = state.copy(authorizationState = AuthorizationState(authorizationState))
- }
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/data/InMemoryCertificateErrorRepository.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/data/InMemoryCertificateErrorRepository.kt
deleted file mode 100644
index 834ec8007dd2d122efaee993640c598fe68d73d1..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/data/InMemoryCertificateErrorRepository.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package app.k9mail.feature.account.setup.data
-
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.CertificateError
-
-class InMemoryCertificateErrorRepository(
- private var certificateError: CertificateError? = null,
-) : DomainContract.CertificateErrorRepository {
-
- override fun getCertificateError(): CertificateError? {
- return certificateError
- }
-
- override fun setCertificateError(certificateError: CertificateError) {
- this.certificateError = certificateError
- }
-
- override fun clearCertificateError() {
- certificateError = null
- }
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapper.kt
index cc99e9eee3ab96ee881e5bd9126b9ea4446017be..6291e02ddeb5b86e30ae9ddaa8f40e626f98852e 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapper.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapper.kt
@@ -4,10 +4,10 @@ import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.autodiscovery.api.IncomingServerSettings
import app.k9mail.autodiscovery.api.OutgoingServerSettings
import app.k9mail.autodiscovery.api.SmtpServerSettings
-import app.k9mail.feature.account.setup.domain.entity.toAuthType
+import app.k9mail.feature.account.common.domain.entity.toAuthType
+import app.k9mail.feature.account.common.domain.entity.toMailConnectionSecurity
import app.k9mail.feature.account.setup.domain.entity.toAuthenticationType
import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.toMailConnectionSecurity
import com.fsck.k9.mail.ServerSettings
internal fun IncomingServerSettings.toServerSettings(password: String?): ServerSettings {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt
index e795f149b7b596480117cd0b59da8f72155bedd7..66c7ff0f365a744382124e4ad6299560a9902056 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt
@@ -2,55 +2,16 @@ package app.k9mail.feature.account.setup.domain
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.CertificateError
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
import com.fsck.k9.mail.ServerSettings
-import com.fsck.k9.mail.server.ServerSettingsValidationResult
-import java.security.cert.X509Certificate
interface DomainContract {
- interface AccountSetupStateRepository {
- fun getState(): AccountSetupState
-
- fun save(accountSetupState: AccountSetupState)
-
- fun saveEmailAddress(emailAddress: String)
-
- fun saveIncomingServerSettings(serverSettings: ServerSettings)
-
- fun saveOutgoingServerSettings(serverSettings: ServerSettings)
-
- fun saveAuthorizationState(authorizationState: AuthorizationState)
-
- fun saveOptions(options: AccountOptions)
-
- fun clear()
- }
-
- interface CertificateErrorRepository {
- fun getCertificateError(): CertificateError?
-
- fun setCertificateError(certificateError: CertificateError)
-
- fun clearCertificateError()
- }
-
interface UseCase {
fun interface GetAutoDiscovery {
suspend fun execute(emailAddress: String): AutoDiscoveryResult
}
- fun interface ValidateServerSettings {
- suspend fun execute(settings: ServerSettings): ServerSettingsValidationResult
- }
-
- fun interface AddServerCertificateException {
- suspend fun addCertificate(hostname: String, port: Int, certificate: X509Certificate?)
- }
-
fun interface CreateAccount {
suspend fun execute(
emailAddress: String,
@@ -65,30 +26,10 @@ interface DomainContract {
fun execute(emailAddress: String): ValidationResult
}
- fun interface ValidatePassword {
- fun execute(password: String): ValidationResult
- }
-
fun interface ValidateConfigurationApproval {
fun execute(isApproved: Boolean?, isAutoDiscoveryTrusted: Boolean?): ValidationResult
}
- fun interface ValidateServer {
- fun execute(server: String): ValidationResult
- }
-
- fun interface ValidatePort {
- fun execute(port: Long?): ValidationResult
- }
-
- fun interface ValidateUsername {
- fun execute(username: String): ValidationResult
- }
-
- fun interface ValidateImapPrefix {
- fun execute(imapPrefix: String): ValidationResult
- }
-
fun interface ValidateAccountName {
fun execute(accountName: String): ValidationResult
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationType.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationType.kt
index a6c2e0be7ed30daf76fa17bd786c867fef555564..ae50f9885ff8fe540c37f7d7fc9900d0919194dc 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationType.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationType.kt
@@ -1,5 +1,7 @@
package app.k9mail.feature.account.setup.domain.entity
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+
typealias AutoDiscoveryAuthenticationType = app.k9mail.autodiscovery.api.AuthenticationType
internal fun AutoDiscoveryAuthenticationType.toAuthenticationType(): AuthenticationType {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurity.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurity.kt
index 8405582849cc36791aea54b9910617df88ca5b44..a5b480ece098ae87c322928a3ac09acdf84b8269 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurity.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurity.kt
@@ -1,5 +1,7 @@
package app.k9mail.feature.account.setup.domain.entity
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
+
internal typealias AutoDiscoveryConnectionSecurity = app.k9mail.autodiscovery.api.ConnectionSecurity
internal fun AutoDiscoveryConnectionSecurity.toConnectionSecurity(): ConnectionSecurity {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtension.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtension.kt
index f45819e1ad976090e7b1ea3b28244f3f9ac1a580..646b71d914004517aaee966cebbe9a9557b37f99 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtension.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtension.kt
@@ -2,6 +2,7 @@ package app.k9mail.feature.account.setup.domain.entity
import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.autodiscovery.api.IncomingServerSettings
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
internal fun IncomingServerSettings.toIncomingProtocolType(): IncomingProtocolType {
when (this) {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/MailConnectionSecurity.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/MailConnectionSecurity.kt
deleted file mode 100644
index 7e4a9efacf5347ae0fc7dc19fc07471c45ba432b..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/entity/MailConnectionSecurity.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package app.k9mail.feature.account.setup.domain.entity
-
-internal typealias MailConnectionSecurity = com.fsck.k9.mail.ConnectionSecurity
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt
index bbcc1975a67c0e303b747ef3de1ed8270e54f430..392ee058fdb450d70d42e4d49c1e1ec9da4d5a4f 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt
@@ -1,14 +1,16 @@
package app.k9mail.feature.account.setup.domain.usecase
+import app.k9mail.feature.account.common.domain.entity.Account
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
-import app.k9mail.feature.account.setup.domain.entity.Account
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
import com.fsck.k9.mail.ServerSettings
+import java.util.UUID
class CreateAccount(
private val accountCreator: AccountCreator,
+ private val uuidGenerator: () -> String = { UUID.randomUUID().toString() },
) : UseCase.CreateAccount {
override suspend fun execute(
emailAddress: String,
@@ -18,6 +20,7 @@ class CreateAccount(
options: AccountOptions,
): String {
val account = Account(
+ uuid = uuidGenerator(),
emailAddress = emailAddress,
incomingServerSettings = incomingServerSettings,
outgoingServerSettings = outgoingServerSettings,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateEmailAddress.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateEmailAddress.kt
index b53451a62126892f1f698c976273b5f439e631d6..3370de6bedfbd74bbadffbbb557fca178f5c4a9a 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateEmailAddress.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/ValidateEmailAddress.kt
@@ -2,9 +2,9 @@ package app.k9mail.feature.account.setup.domain.usecase
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
-internal class ValidateEmailAddress : DomainContract.UseCase.ValidateEmailAddress {
+class ValidateEmailAddress : UseCase.ValidateEmailAddress {
// TODO replace by new email validation
override fun execute(emailAddress: String): ValidationResult {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt
index a4e95c1fdf81ca9d747b27e6c04177770e77a88d..5d4eb2ce88cc3367503e5a8c04067c94fd4a3b28 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavigation.kt
@@ -3,10 +3,10 @@ package app.k9mail.feature.account.setup.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
-import androidx.navigation.compose.composable
+import app.k9mail.core.ui.compose.common.navigation.deepLinkComposable
import app.k9mail.feature.account.setup.ui.AccountSetupScreen
-const val NAVIGATION_ROUTE_ACCOUNT_SETUP = "/account/setup"
+const val NAVIGATION_ROUTE_ACCOUNT_SETUP = "account/setup"
fun NavController.navigateToAccountSetup(navOptions: NavOptions? = null) {
navigate(NAVIGATION_ROUTE_ACCOUNT_SETUP, navOptions)
@@ -16,7 +16,7 @@ fun NavGraphBuilder.accountSetupRoute(
onBack: () -> Unit,
onFinish: (String) -> Unit,
) {
- composable(route = NAVIGATION_ROUTE_ACCOUNT_SETUP) {
+ deepLinkComposable(route = NAVIGATION_ROUTE_ACCOUNT_SETUP) {
AccountSetupScreen(
onBack = onBack,
onFinish = onFinish,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupContract.kt
index 8d8651de75fd2cb50386c77195bfe77d98dee300..7caefbdd46a20c96ea56d30ce57f056e017d1d3d 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupContract.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupContract.kt
@@ -21,12 +21,11 @@ interface AccountSetupContract {
)
sealed interface Event {
- object OnNext : Event
-
data class OnAutoDiscoveryFinished(
val isAutomaticConfig: Boolean,
) : Event
+ object OnNext : Event
object OnBack : Event
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreen.kt
index 91eb2b013c22ab639d4f4900b16dd1f96824ffe2..98b96ce9ac81600287c0449595c8bb5fbfbda1e6 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreen.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreen.kt
@@ -3,8 +3,16 @@ package app.k9mail.feature.account.setup.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.common.mvi.observe
-import app.k9mail.feature.account.setup.NAME_INCOMING_VALIDATION
-import app.k9mail.feature.account.setup.NAME_OUTGOING_VALIDATION
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsScreen
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsViewModel
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsScreen
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsViewModel
+import app.k9mail.feature.account.server.validation.ui.IncomingServerValidationViewModel
+import app.k9mail.feature.account.server.validation.ui.OutgoingServerValidationViewModel
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationScreen
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Event
import app.k9mail.feature.account.setup.ui.AccountSetupContract.SetupStep
@@ -12,20 +20,10 @@ import app.k9mail.feature.account.setup.ui.AccountSetupContract.ViewModel
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryScreen
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryViewModel
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigScreen
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigViewModel
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract
import app.k9mail.feature.account.setup.ui.options.AccountOptionsScreen
import app.k9mail.feature.account.setup.ui.options.AccountOptionsViewModel
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigScreen
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigViewModel
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationScreen
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationViewModel
import org.koin.androidx.compose.koinViewModel
-import org.koin.core.qualifier.named
@Suppress("LongMethod")
@Composable
@@ -34,14 +32,12 @@ fun AccountSetupScreen(
onBack: () -> Unit,
viewModel: ViewModel = koinViewModel(),
autoDiscoveryViewModel: AccountAutoDiscoveryContract.ViewModel = koinViewModel(),
- incomingViewModel: AccountIncomingConfigContract.ViewModel = koinViewModel(),
- incomingValidationViewModel: AccountValidationContract.ViewModel = koinViewModel(
- named(NAME_INCOMING_VALIDATION),
- ),
- outgoingViewModel: AccountOutgoingConfigContract.ViewModel = koinViewModel(),
- outgoingValidationViewModel: AccountValidationContract.ViewModel = koinViewModel(
- named(NAME_OUTGOING_VALIDATION),
- ),
+ incomingViewModel: IncomingServerSettingsContract.ViewModel = koinViewModel(),
+ incomingValidationViewModel: ServerValidationContract.ViewModel =
+ koinViewModel(),
+ outgoingViewModel: OutgoingServerSettingsContract.ViewModel = koinViewModel(),
+ outgoingValidationViewModel: ServerValidationContract.ViewModel =
+ koinViewModel(),
optionsViewModel: AccountOptionsContract.ViewModel = koinViewModel(),
) {
val (state, dispatch) = viewModel.observe { effect ->
@@ -67,7 +63,7 @@ fun AccountSetupScreen(
}
SetupStep.INCOMING_CONFIG -> {
- AccountIncomingConfigScreen(
+ IncomingServerSettingsScreen(
onNext = { dispatch(Event.OnNext) },
onBack = { dispatch(Event.OnBack) },
viewModel = incomingViewModel,
@@ -75,7 +71,7 @@ fun AccountSetupScreen(
}
SetupStep.INCOMING_VALIDATION -> {
- AccountValidationScreen(
+ ServerValidationScreen(
onNext = { dispatch(Event.OnNext) },
onBack = { dispatch(Event.OnBack) },
viewModel = incomingValidationViewModel,
@@ -83,7 +79,7 @@ fun AccountSetupScreen(
}
SetupStep.OUTGOING_CONFIG -> {
- AccountOutgoingConfigScreen(
+ OutgoingServerSettingsScreen(
onNext = { dispatch(Event.OnNext) },
onBack = { dispatch(Event.OnBack) },
viewModel = outgoingViewModel,
@@ -91,7 +87,7 @@ fun AccountSetupScreen(
}
SetupStep.OUTGOING_VALIDATION -> {
- AccountValidationScreen(
+ ServerValidationScreen(
onNext = { dispatch(Event.OnNext) },
onBack = { dispatch(Event.OnBack) },
viewModel = outgoingValidationViewModel,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModel.kt
index 8e3d36466b48143e4118e3131cfe43ce36e69f23..5c38c8a25c8f689cfa48d8242e194bf41da172b7 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModel.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModel.kt
@@ -2,8 +2,8 @@ package app.k9mail.feature.account.setup.ui
import androidx.lifecycle.viewModelScope
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Event
@@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
@Suppress("LongParameterList")
class AccountSetupViewModel(
private val createAccount: UseCase.CreateAccount,
- private val accountSetupStateRepository: DomainContract.AccountSetupStateRepository,
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
initialState: State = State(),
) : BaseViewModel(initialState), AccountSetupContract.ViewModel {
@@ -104,7 +104,7 @@ class AccountSetupViewModel(
private fun changeToSetupStep(setupStep: SetupStep) {
if (setupStep == SetupStep.AUTO_CONFIG) {
- accountSetupStateRepository.saveAuthorizationState(AuthorizationState(null))
+ accountStateRepository.setAuthorizationState(AuthorizationState(null))
}
updateState {
@@ -115,15 +115,15 @@ class AccountSetupViewModel(
}
private fun onFinish() {
- val accountSetupState = accountSetupStateRepository.getState()
+ val accountState = accountStateRepository.getState()
viewModelScope.launch {
val result = createAccount.execute(
- emailAddress = accountSetupState.emailAddress ?: "",
- incomingServerSettings = accountSetupState.incomingServerSettings!!,
- outgoingServerSettings = accountSetupState.outgoingServerSettings!!,
- authorizationState = accountSetupState.authorizationState?.state,
- options = accountSetupState.options!!,
+ emailAddress = accountState.emailAddress ?: "",
+ incomingServerSettings = accountState.incomingServerSettings!!,
+ outgoingServerSettings = accountState.outgoingServerSettings!!,
+ authorizationState = accountState.authorizationState?.state,
+ options = accountState.options!!,
)
navigateNext(result)
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt
index a0dd9b217ed8197a35b81b626a5d6141c68cbf2c..1b1a48b8832f24b0e94fca4178c4fa9a2aea940f 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt
@@ -8,18 +8,21 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import app.k9mail.core.ui.compose.common.DevicePreviews
+import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorState
+import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
+import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
+import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
-import app.k9mail.feature.account.common.ui.item.ErrorItem
-import app.k9mail.feature.account.common.ui.item.LoadingItem
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
import app.k9mail.feature.account.oauth.ui.preview.PreviewAccountOAuthViewModel
import app.k9mail.feature.account.setup.R
@@ -43,28 +46,37 @@ internal fun AccountAutoDiscoveryContent(
.then(modifier),
) {
val resources = LocalContext.current.resources
+ val viewState = remember(key1 = state.isLoading, key2 = state.error) {
+ when {
+ state.isLoading -> ContentLoadingErrorState.Loading
+ state.error != null -> ContentLoadingErrorState.Error
+ else -> ContentLoadingErrorState.Content
+ }
+ }
- LazyColumn(
- modifier = Modifier
- .fillMaxSize()
- .imePadding(),
- verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double, Alignment.CenterVertically),
+ ContentLoadingErrorView(
+ state = viewState,
+ loading = {
+ LoadingView(
+ message = stringResource(id = R.string.account_setup_auto_discovery_loading_message),
+ modifier = Modifier.fillMaxSize(),
+ )
+ },
+ error = {
+ ErrorView(
+ title = stringResource(id = R.string.account_setup_auto_discovery_loading_error),
+ message = state.error?.toResourceString(resources),
+ onRetry = { onEvent(Event.OnRetryClicked) },
+ modifier = Modifier.fillMaxSize(),
+ )
+ },
) {
- if (state.isLoading) {
- item(key = "loading") {
- LoadingItem(
- message = stringResource(id = R.string.account_setup_auto_config_loading_message),
- )
- }
- } else if (state.error != null) {
- item(key = "error") {
- ErrorItem(
- title = stringResource(id = R.string.account_setup_auto_config_loading_error),
- message = state.error.toResourceString(resources),
- onRetry = { onEvent(Event.OnRetryClicked) },
- )
- }
- } else {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .imePadding(),
+ verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double, Alignment.CenterVertically),
+ ) {
contentItems(
state = state,
onEvent = onEvent,
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContract.kt
index 346b34d537bd9cbe279186adf7f810480bbb8975..287bfdae1c60ac7be06c5070957cc150319a1234 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContract.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContract.kt
@@ -3,11 +3,11 @@ package app.k9mail.feature.account.setup.ui.autodiscovery
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.entity.AuthorizationState
+import app.k9mail.feature.account.common.domain.input.BooleanInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.oauth.domain.entity.OAuthResult
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
-import app.k9mail.feature.account.setup.domain.input.BooleanInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
interface AccountAutoDiscoveryContract {
@@ -41,7 +41,7 @@ interface AccountAutoDiscoveryContract {
sealed interface Event {
data class EmailAddressChanged(val emailAddress: String) : Event
data class PasswordChanged(val password: String) : Event
- data class ConfigurationApprovalChanged(val confirmed: Boolean) : Event
+ data class ResultApprovalChanged(val confirmed: Boolean) : Event
data class OnOAuthResult(val result: OAuthResult) : Event
object OnNextClicked : Event
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryScreen.kt
index b533c38788a073c2501c9a4891bf7792f51a448b..d5fd3ab63ba9e50b8483445055806fc2350b8ac4 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryScreen.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryScreen.kt
@@ -13,12 +13,12 @@ import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
import app.k9mail.feature.account.common.ui.AppTitleTopHeader
import app.k9mail.feature.account.common.ui.WizardNavigationBar
import app.k9mail.feature.account.common.ui.WizardNavigationBarState
+import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository
import app.k9mail.feature.account.oauth.ui.preview.PreviewAccountOAuthViewModel
import app.k9mail.feature.account.setup.R
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Effect
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Event
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.ViewModel
-import app.k9mail.feature.account.setup.ui.preview.PreviewAccountSetupStateRepository
@Composable
internal fun AccountAutoDiscoveryScreen(
@@ -46,8 +46,6 @@ internal fun AccountAutoDiscoveryScreen(
},
bottomBar = {
WizardNavigationBar(
- nextButtonText = stringResource(id = R.string.account_setup_button_next),
- backButtonText = stringResource(id = R.string.account_setup_button_back),
onNextClick = { dispatch(Event.OnNextClicked) },
onBackClick = { dispatch(Event.OnBackClicked) },
state = WizardNavigationBarState(showNext = state.value.isNextButtonVisible),
@@ -74,7 +72,7 @@ internal fun AccountAutoDiscoveryScreenK9Preview() {
viewModel = AccountAutoDiscoveryViewModel(
validator = AccountAutoDiscoveryValidator(),
getAutoDiscovery = { AutoDiscoveryResult.NoUsableSettingsFound },
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ accountStateRepository = PreviewAccountStateRepository(),
oAuthViewModel = PreviewAccountOAuthViewModel(),
),
)
@@ -91,7 +89,7 @@ internal fun AccountAutoDiscoveryScreenThunderbirdPreview() {
viewModel = AccountAutoDiscoveryViewModel(
validator = AccountAutoDiscoveryValidator(),
getAutoDiscovery = { AutoDiscoveryResult.NoUsableSettingsFound },
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ accountStateRepository = PreviewAccountStateRepository(),
oAuthViewModel = PreviewAccountOAuthViewModel(),
),
)
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt
index 27ad77184e11ddbd3f134e6af30b17d05397bc85..7aa4f6846ed96108ed259c0a45716f825bf61083 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt
@@ -2,19 +2,19 @@ package app.k9mail.feature.account.setup.ui.autodiscovery
import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.autodiscovery.api.SmtpServerSettings
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
import app.k9mail.feature.account.setup.domain.entity.toAuthenticationType
import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity
import app.k9mail.feature.account.setup.domain.entity.toIncomingProtocolType
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
import app.k9mail.feature.account.setup.domain.toServerSettings
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract
-internal fun AccountAutoDiscoveryContract.State.toAccountSetupState(): AccountSetupState {
- return AccountSetupState(
+internal fun AccountAutoDiscoveryContract.State.toAccountState(): AccountState {
+ return AccountState(
emailAddress = emailAddress.value,
incomingServerSettings = autoDiscoverySettings?.incomingServerSettings?.toServerSettings(password.value),
outgoingServerSettings = autoDiscoverySettings?.outgoingServerSettings?.toServerSettings(password.value),
@@ -23,15 +23,15 @@ internal fun AccountAutoDiscoveryContract.State.toAccountSetupState(): AccountSe
)
}
-internal fun AccountAutoDiscoveryContract.State.toIncomingConfigState(): AccountIncomingConfigContract.State {
+internal fun AccountAutoDiscoveryContract.State.toIncomingConfigState(): IncomingServerSettingsContract.State {
val incomingSettings = autoDiscoverySettings?.incomingServerSettings as? ImapServerSettings?
return if (incomingSettings == null) {
- AccountIncomingConfigContract.State(
+ IncomingServerSettingsContract.State(
username = StringInputField(value = emailAddress.value),
password = StringInputField(value = password.value),
)
} else {
- AccountIncomingConfigContract.State(
+ IncomingServerSettingsContract.State(
protocolType = incomingSettings.toIncomingProtocolType(),
server = StringInputField(value = incomingSettings.hostname.value),
security = incomingSettings.connectionSecurity.toConnectionSecurity(),
@@ -43,15 +43,15 @@ internal fun AccountAutoDiscoveryContract.State.toIncomingConfigState(): Account
}
}
-internal fun AccountAutoDiscoveryContract.State.toOutgoingConfigState(): AccountOutgoingConfigContract.State {
+internal fun AccountAutoDiscoveryContract.State.toOutgoingConfigState(): OutgoingServerSettingsContract.State {
val outgoingSettings = autoDiscoverySettings?.outgoingServerSettings as? SmtpServerSettings?
return if (outgoingSettings == null) {
- AccountOutgoingConfigContract.State(
+ OutgoingServerSettingsContract.State(
username = StringInputField(value = emailAddress.value),
password = StringInputField(value = password.value),
)
} else {
- AccountOutgoingConfigContract.State(
+ OutgoingServerSettingsContract.State(
server = StringInputField(value = outgoingSettings.hostname.value),
security = outgoingSettings.connectionSecurity.toConnectionSecurity(),
port = NumberInputField(value = outgoingSettings.port.value.toLong()),
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryValidator.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryValidator.kt
index 2ae80059e509faa8599ce6b8fe667839493ba861..72dbeeab48bcfa2276ce022c2ad48f57dba738ca 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryValidator.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryValidator.kt
@@ -1,14 +1,15 @@
package app.k9mail.feature.account.setup.ui.autodiscovery
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
+import app.k9mail.feature.account.server.settings.domain.usecase.ValidatePassword
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
import app.k9mail.feature.account.setup.domain.usecase.ValidateConfigurationApproval
import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailAddress
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword
+import app.k9mail.feature.account.server.settings.domain.ServerSettingsDomainContract.UseCase as ServerSettingsUseCase
internal class AccountAutoDiscoveryValidator(
private val emailAddressValidator: UseCase.ValidateEmailAddress = ValidateEmailAddress(),
- private val passwordValidator: UseCase.ValidatePassword = ValidatePassword(),
+ private val passwordValidator: ServerSettingsUseCase.ValidatePassword = ValidatePassword(),
private val configurationApprovalValidator: UseCase.ValidateConfigurationApproval = ValidateConfigurationApproval(),
) : AccountAutoDiscoveryContract.Validator {
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModel.kt
index fec25ba15cb302422e7c1c8aa4958c8b8a15d812..29b774fda3c7299ec5df307cd795242bb2ab5c0d 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModel.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModel.kt
@@ -5,12 +5,12 @@ import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.oauth.domain.entity.OAuthResult
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
-import app.k9mail.feature.account.setup.domain.DomainContract
import app.k9mail.feature.account.setup.domain.DomainContract.UseCase
import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryAuthenticationType
-import app.k9mail.feature.account.setup.domain.input.StringInputField
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.ConfigStep
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Effect
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Error
@@ -24,7 +24,7 @@ internal class AccountAutoDiscoveryViewModel(
initialState: State = State(),
private val validator: Validator,
private val getAutoDiscovery: UseCase.GetAutoDiscovery,
- private val accountSetupStateRepository: DomainContract.AccountSetupStateRepository,
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
override val oAuthViewModel: AccountOAuthContract.ViewModel,
) : BaseViewModel(initialState), AccountAutoDiscoveryContract.ViewModel {
@@ -38,7 +38,7 @@ internal class AccountAutoDiscoveryViewModel(
when (event) {
is Event.EmailAddressChanged -> changeEmailAddress(event.emailAddress)
is Event.PasswordChanged -> changePassword(event.password)
- is Event.ConfigurationApprovalChanged -> changeConfigurationApproval(event.confirmed)
+ is Event.ResultApprovalChanged -> changeConfigurationApproval(event.confirmed)
is Event.OnOAuthResult -> onOAuthResult(event.result)
Event.OnNextClicked -> onNext()
@@ -51,7 +51,7 @@ internal class AccountAutoDiscoveryViewModel(
}
private fun changeEmailAddress(emailAddress: String) {
- accountSetupStateRepository.clear()
+ accountStateRepository.clear()
updateState {
State(
emailAddress = StringInputField(value = emailAddress),
@@ -250,7 +250,7 @@ internal class AccountAutoDiscoveryViewModel(
private fun navigateBack() = emitEffect(Effect.NavigateBack)
private fun navigateNext(isAutomaticConfig: Boolean) {
- accountSetupStateRepository.save(state.value.toAccountSetupState())
+ accountStateRepository.setState(state.value.toAccountState())
emitEffect(Effect.NavigateNext(isAutomaticConfig))
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AutoDiscoveryStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AutoDiscoveryStringMapper.kt
index 19d8749ce7b9679f79b6631650822dbfbeb49dde..9ec6756f5f9c66c24c787112a655cb285ddc5f0a 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AutoDiscoveryStringMapper.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AutoDiscoveryStringMapper.kt
@@ -5,15 +5,16 @@ import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.feature.account.setup.R
import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryConnectionSecurity
import app.k9mail.feature.account.setup.domain.usecase.ValidateConfigurationApproval
+import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailAddress
internal fun AutoDiscoveryConnectionSecurity.toResourceString(resources: Resources): String {
return when (this) {
AutoDiscoveryConnectionSecurity.StartTLS -> resources.getString(
- R.string.account_setup_connection_security_start_tls,
+ R.string.account_setup_auto_discovery_connection_security_start_tls,
)
AutoDiscoveryConnectionSecurity.TLS -> resources.getString(
- R.string.account_setup_connection_security_ssl,
+ R.string.account_setup_auto_discovery_connection_security_ssl,
)
}
}
@@ -27,6 +28,7 @@ internal fun AccountAutoDiscoveryContract.Error.toResourceString(resources: Reso
internal fun ValidationError.toResourceString(resources: Resources): String {
return when (this) {
+ is ValidateEmailAddress.ValidateEmailAddressError -> toEmailAddressErrorString(resources)
is ValidateConfigurationApproval.ValidateConfigurationApprovalError -> toConfigurationApprovalErrorString(
resources,
)
@@ -35,12 +37,24 @@ internal fun ValidationError.toResourceString(resources: Resources): String {
}
}
+private fun ValidateEmailAddress.ValidateEmailAddressError.toEmailAddressErrorString(resources: Resources): String {
+ return when (this) {
+ is ValidateEmailAddress.ValidateEmailAddressError.EmptyEmailAddress -> resources.getString(
+ R.string.account_setup_auto_discovery_validation_error_email_address_required,
+ )
+
+ is ValidateEmailAddress.ValidateEmailAddressError.InvalidEmailAddress -> resources.getString(
+ R.string.account_setup_auto_discovery_validation_error_email_address_invalid,
+ )
+ }
+}
+
private fun ValidateConfigurationApproval.ValidateConfigurationApprovalError.toConfigurationApprovalErrorString(
resources: Resources,
): String {
return when (this) {
ValidateConfigurationApproval.ValidateConfigurationApprovalError.ApprovalRequired -> resources.getString(
- R.string.account_setup_error_configuration_approval_required,
+ R.string.account_setup_auto_discovery_result_approval_error_approval_required,
)
}
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ConfigurationApprovalItem.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryResultApprovalItem.kt
similarity index 54%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ConfigurationApprovalItem.kt
rename to feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryResultApprovalItem.kt
index dc7bac30968507843fd67dba702551d24a25ad40..7a98900024952e7c229df58636b323b5b0c86fcd 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ConfigurationApprovalItem.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryResultApprovalItem.kt
@@ -3,22 +3,22 @@ package app.k9mail.feature.account.setup.ui.autodiscovery.item
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import app.k9mail.feature.account.common.domain.input.BooleanInputField
import app.k9mail.feature.account.common.ui.item.ListItem
-import app.k9mail.feature.account.setup.domain.input.BooleanInputField
-import app.k9mail.feature.account.setup.ui.autodiscovery.view.ConfigurationApprovalView
+import app.k9mail.feature.account.setup.ui.autodiscovery.view.AutoDiscoveryResultApprovalView
@Composable
-internal fun LazyItemScope.ConfigurationApprovalItem(
+internal fun LazyItemScope.AutoDiscoveryResultApprovalItem(
approvalState: BooleanInputField,
- onConfigurationApprovalChange: (Boolean) -> Unit,
+ onApprovalChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
ListItem(
modifier = modifier,
) {
- ConfigurationApprovalView(
+ AutoDiscoveryResultApprovalView(
approvalState = approvalState,
- onConfigurationApprovalChange = onConfigurationApprovalChange,
+ onApprovalChange = onApprovalChange,
)
}
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryStatusItem.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryResultItem.kt
similarity index 86%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryStatusItem.kt
rename to feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryResultItem.kt
index 532ac67e93281e50ac9c7d18e28a8a63f7547480..bc550dfe9b3a008324630a84d4a119efbae3811a 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryStatusItem.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/AutoDiscoveryResultItem.kt
@@ -5,10 +5,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.feature.account.common.ui.item.ListItem
-import app.k9mail.feature.account.setup.ui.autodiscovery.view.AutoDiscoveryStatusView
+import app.k9mail.feature.account.setup.ui.autodiscovery.view.AutoDiscoveryResultView
@Composable
-internal fun LazyItemScope.AutoDiscoveryStatusItem(
+internal fun LazyItemScope.AutoDiscoveryResultItem(
autoDiscoverySettings: AutoDiscoveryResult.Settings?,
onEditConfigurationClick: () -> Unit,
modifier: Modifier = Modifier,
@@ -16,7 +16,7 @@ internal fun LazyItemScope.AutoDiscoveryStatusItem(
ListItem(
modifier = modifier,
) {
- AutoDiscoveryStatusView(
+ AutoDiscoveryResultView(
settings = autoDiscoverySettings,
onEditConfigurationClick = onEditConfigurationClick,
)
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ContentItems.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ContentItems.kt
index 331a153a2ca8ab15cecdbfbd528903c1f4bad88b..bf6c92e495c9506946ffb3454c2c02d11a44a13e 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ContentItems.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/ContentItems.kt
@@ -15,16 +15,16 @@ internal fun LazyListScope.contentItems(
) {
if (state.configStep != ConfigStep.EMAIL_ADDRESS) {
item(key = "autodiscovery") {
- AutoDiscoveryStatusItem(
+ AutoDiscoveryResultItem(
autoDiscoverySettings = state.autoDiscoverySettings,
onEditConfigurationClick = { onEvent(Event.OnEditConfigurationClicked) },
)
}
if (state.autoDiscoverySettings != null && state.autoDiscoverySettings.isTrusted.not()) {
- item(key = "config_approval") {
- ConfigurationApprovalItem(
+ item(key = "result_approval") {
+ AutoDiscoveryResultApprovalItem(
approvalState = state.configurationApproved,
- onConfigurationApprovalChange = { onEvent(Event.ConfigurationApprovalChanged(it)) },
+ onApprovalChange = { onEvent(Event.ResultApprovalChanged(it)) },
)
}
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/EmailAddressItem.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/EmailAddressItem.kt
index 6e4d13b586fb77ec79234cf8d6a7ea38c63edce5..b4fd9d522f8ca747a95afb61685c0a1f3413ff02 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/EmailAddressItem.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/EmailAddressItem.kt
@@ -8,7 +8,7 @@ import androidx.compose.ui.platform.LocalContext
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.ui.compose.designsystem.molecule.input.EmailAddressInput
import app.k9mail.feature.account.common.ui.item.ListItem
-import app.k9mail.feature.account.setup.ui.common.toResourceString
+import app.k9mail.feature.account.server.settings.ui.common.mapper.toResourceString
@Composable
internal fun LazyItemScope.EmailAddressItem(
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/PasswordItem.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/PasswordItem.kt
index a606f9d483a1299724e11bd40cebc645e0f816b2..2b5447989cf8c7f3f12c4443e7bdcfa93700c2b5 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/PasswordItem.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/item/PasswordItem.kt
@@ -8,7 +8,7 @@ import androidx.compose.ui.platform.LocalContext
import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.ui.compose.designsystem.molecule.input.PasswordInput
import app.k9mail.feature.account.common.ui.item.ListItem
-import app.k9mail.feature.account.setup.ui.common.toResourceString
+import app.k9mail.feature.account.server.settings.ui.common.mapper.toResourceString
@Composable
internal fun LazyItemScope.PasswordItem(
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/ConfigurationApprovalView.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultApprovalView.kt
similarity index 58%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/ConfigurationApprovalView.kt
rename to feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultApprovalView.kt
index 2bd6c81b27df2fdd9e22907afedfe0b8351fa199..b1ab4bc1c5c7095709adbf27d148376ba9877550 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/ConfigurationApprovalView.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultApprovalView.kt
@@ -7,16 +7,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.designsystem.molecule.input.CheckboxInput
import app.k9mail.core.ui.compose.theme.MainTheme
+import app.k9mail.core.ui.compose.theme.PreviewWithThemes
+import app.k9mail.feature.account.common.domain.input.BooleanInputField
import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.domain.input.BooleanInputField
import app.k9mail.feature.account.setup.ui.autodiscovery.toResourceString
@Composable
-internal fun ConfigurationApprovalView(
+internal fun AutoDiscoveryResultApprovalView(
approvalState: BooleanInputField,
- onConfigurationApprovalChange: (Boolean) -> Unit,
+ onApprovalChange: (Boolean) -> Unit,
) {
val resources = LocalContext.current.resources
@@ -24,11 +26,25 @@ internal fun ConfigurationApprovalView(
CheckboxInput(
text = stringResource(
- id = R.string.account_setup_auto_config_status_checkbox_configuration_untrusted_confirmation_label,
+ id = R.string.account_setup_auto_discovery_result_approval_checkbox_label,
),
checked = approvalState.value ?: false,
- onCheckedChange = onConfigurationApprovalChange,
+ onCheckedChange = onApprovalChange,
errorMessage = approvalState.error?.toResourceString(resources),
contentPadding = PaddingValues(),
)
}
+
+@Preview(showBackground = true)
+@Composable
+internal fun AutoDiscoveryResultApprovalViewPreview() {
+ PreviewWithThemes {
+ AutoDiscoveryResultApprovalView(
+ approvalState = BooleanInputField(
+ value = true,
+ isValid = true,
+ ),
+ onApprovalChange = {},
+ )
+ }
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusBodyView.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultBodyView.kt
similarity index 94%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusBodyView.kt
rename to feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultBodyView.kt
index a037618f5e239da126641891e676a12f6c464f21..edc2cc115727b2a4977bfce0424a248f6b319bec 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusBodyView.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultBodyView.kt
@@ -26,7 +26,7 @@ import app.k9mail.core.ui.compose.theme.PreviewWithThemes
import app.k9mail.feature.account.setup.R
@Composable
-internal fun AutoDiscoveryStatusBodyView(
+internal fun AutoDiscoveryResultBodyView(
settings: AutoDiscoveryResult.Settings,
onEditConfigurationClick: () -> Unit,
modifier: Modifier = Modifier,
@@ -42,7 +42,7 @@ internal fun AutoDiscoveryStatusBodyView(
Spacer(modifier = Modifier.height(MainTheme.sizes.smaller))
TextBody2(
text = stringResource(
- id = R.string.account_setup_auto_config_status_disclaimer_untrusted_configuration,
+ id = R.string.account_setup_auto_discovery_result_disclaimer_untrusted_configuration,
),
modifier = Modifier.fillMaxWidth(),
)
@@ -94,7 +94,7 @@ internal fun EditConfigurationButton(
.then(modifier),
) {
ButtonText(
- text = stringResource(id = R.string.account_setup_auto_config_status_edit_configuration_button_label),
+ text = stringResource(id = R.string.account_setup_auto_discovery_result_edit_configuration_button_label),
onClick = onEditConfigurationClick,
color = MainTheme.colors.warning,
contentPadding = buttonContentPadding(
@@ -107,9 +107,9 @@ internal fun EditConfigurationButton(
@Preview
@Composable
-internal fun AutoDiscoveryStatusBodyViewPreview() {
+internal fun AutoDiscoveryResultBodyViewPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusBodyView(
+ AutoDiscoveryResultBodyView(
settings = AutoDiscoveryResult.Settings(
incomingServerSettings = ImapServerSettings(
hostname = "imap.example.com".toHostname(),
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultHeaderState.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultHeaderState.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8a6cf132be794c5b2e6fccbd00222d799c556460
--- /dev/null
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultHeaderState.kt
@@ -0,0 +1,34 @@
+package app.k9mail.feature.account.setup.ui.autodiscovery.view
+
+import androidx.annotation.StringRes
+import androidx.compose.ui.graphics.vector.ImageVector
+import app.k9mail.core.ui.compose.theme.Icons
+import app.k9mail.feature.account.setup.R
+
+enum class AutoDiscoveryResultHeaderState(
+ val icon: ImageVector,
+ @StringRes val titleResourceId: Int,
+ @StringRes val subtitleResourceId: Int,
+ val isExpandable: Boolean,
+) {
+ NoSettings(
+ icon = Icons.Outlined.info,
+ titleResourceId = R.string.account_setup_auto_discovery_result_header_title_configuration_not_found,
+ subtitleResourceId = R.string.account_setup_auto_discovery_result_header_subtitle_configuration_not_found,
+ isExpandable = false,
+ ),
+
+ Trusted(
+ icon = Icons.Outlined.check,
+ titleResourceId = R.string.account_setup_auto_discovery_status_header_title_configuration_found,
+ subtitleResourceId = R.string.account_setup_auto_discovery_result_header_subtitle_configuration_trusted,
+ isExpandable = true,
+ ),
+
+ Untrusted(
+ icon = Icons.Outlined.info,
+ titleResourceId = R.string.account_setup_auto_discovery_status_header_title_configuration_found,
+ subtitleResourceId = R.string.account_setup_auto_discovery_result_header_subtitle_configuration_untrusted,
+ isExpandable = true,
+ ),
+}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusHeaderView.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultHeaderView.kt
similarity index 70%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusHeaderView.kt
rename to feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultHeaderView.kt
index 31c5906027b294db23b00ff476ec414681fc1bc1..8bfd4c3d41bd15a73fb67ed7ad545226ddcd2a4d 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusHeaderView.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultHeaderView.kt
@@ -20,8 +20,8 @@ import app.k9mail.core.ui.compose.theme.PreviewWithThemes
@Suppress("LongMethod")
@Composable
-internal fun AutoDiscoveryStatusHeaderView(
- state: AutoDiscoveryStatusHeaderState,
+internal fun AutoDiscoveryResultHeaderView(
+ state: AutoDiscoveryResultHeaderState,
isExpanded: Boolean,
modifier: Modifier = Modifier,
) {
@@ -65,20 +65,20 @@ internal fun AutoDiscoveryStatusHeaderView(
}
@Composable
-private fun selectColor(state: AutoDiscoveryStatusHeaderState): Color {
+private fun selectColor(state: AutoDiscoveryResultHeaderState): Color {
return when (state) {
- AutoDiscoveryStatusHeaderState.NoSettings -> MainTheme.colors.primary
- AutoDiscoveryStatusHeaderState.Trusted -> MainTheme.colors.success
- AutoDiscoveryStatusHeaderState.Untrusted -> MainTheme.colors.warning
+ AutoDiscoveryResultHeaderState.NoSettings -> MainTheme.colors.primary
+ AutoDiscoveryResultHeaderState.Trusted -> MainTheme.colors.success
+ AutoDiscoveryResultHeaderState.Untrusted -> MainTheme.colors.warning
}
}
@Preview
@Composable
-internal fun AutoDiscoveryStatusHeaderViewTrustedCollapsedPreview() {
+internal fun AutoDiscoveryResultHeaderViewTrustedCollapsedPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusHeaderView(
- state = AutoDiscoveryStatusHeaderState.Trusted,
+ AutoDiscoveryResultHeaderView(
+ state = AutoDiscoveryResultHeaderState.Trusted,
isExpanded = true,
)
}
@@ -86,10 +86,10 @@ internal fun AutoDiscoveryStatusHeaderViewTrustedCollapsedPreview() {
@Preview
@Composable
-internal fun AutoDiscoveryStatusHeaderViewTrustedExpandedPreview() {
+internal fun AutoDiscoveryResultHeaderViewTrustedExpandedPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusHeaderView(
- state = AutoDiscoveryStatusHeaderState.Trusted,
+ AutoDiscoveryResultHeaderView(
+ state = AutoDiscoveryResultHeaderState.Trusted,
isExpanded = false,
)
}
@@ -97,10 +97,10 @@ internal fun AutoDiscoveryStatusHeaderViewTrustedExpandedPreview() {
@Preview
@Composable
-internal fun AutoDiscoveryStatusHeaderViewUntrustedCollapsedPreview() {
+internal fun AutoDiscoveryResultHeaderViewUntrustedCollapsedPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusHeaderView(
- state = AutoDiscoveryStatusHeaderState.Untrusted,
+ AutoDiscoveryResultHeaderView(
+ state = AutoDiscoveryResultHeaderState.Untrusted,
isExpanded = true,
)
}
@@ -108,10 +108,10 @@ internal fun AutoDiscoveryStatusHeaderViewUntrustedCollapsedPreview() {
@Preview
@Composable
-internal fun AutoDiscoveryStatusHeaderViewUntrustedExpandedPreview() {
+internal fun AutoDiscoveryResultHeaderViewUntrustedExpandedPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusHeaderView(
- state = AutoDiscoveryStatusHeaderState.Untrusted,
+ AutoDiscoveryResultHeaderView(
+ state = AutoDiscoveryResultHeaderState.Untrusted,
isExpanded = false,
)
}
@@ -119,10 +119,10 @@ internal fun AutoDiscoveryStatusHeaderViewUntrustedExpandedPreview() {
@Preview
@Composable
-internal fun AutoDiscoveryStatusHeaderNoSettingsPreview() {
+internal fun AutoDiscoveryResultHeaderNoSettingsPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusHeaderView(
- state = AutoDiscoveryStatusHeaderState.NoSettings,
+ AutoDiscoveryResultHeaderView(
+ state = AutoDiscoveryResultHeaderState.NoSettings,
isExpanded = false,
)
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusView.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultView.kt
similarity index 89%
rename from feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusView.kt
rename to feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultView.kt
index 4d840f2b9fbf34854e2737b888947f646c3720bf..5096b99a478f8cff4040fb773062bae4e3f46ed3 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusView.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryResultView.kt
@@ -24,7 +24,7 @@ import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
@Composable
-internal fun AutoDiscoveryStatusView(
+internal fun AutoDiscoveryResultView(
settings: AutoDiscoveryResult.Settings?,
onEditConfigurationClick: () -> Unit,
modifier: Modifier = Modifier,
@@ -47,20 +47,20 @@ internal fun AutoDiscoveryStatusView(
Column(
modifier = Modifier.padding(MainTheme.spacings.default),
) {
- AutoDiscoveryStatusHeaderView(
+ AutoDiscoveryResultHeaderView(
state = if (settings == null) {
- AutoDiscoveryStatusHeaderState.NoSettings
+ AutoDiscoveryResultHeaderState.NoSettings
} else if (settings.isTrusted) {
- AutoDiscoveryStatusHeaderState.Trusted
+ AutoDiscoveryResultHeaderState.Trusted
} else {
- AutoDiscoveryStatusHeaderState.Untrusted
+ AutoDiscoveryResultHeaderState.Untrusted
},
isExpanded = expanded.value,
)
if (settings != null) {
AnimatedVisibility(visible = expanded.value) {
- AutoDiscoveryStatusBodyView(
+ AutoDiscoveryResultBodyView(
settings = settings,
onEditConfigurationClick = onEditConfigurationClick,
)
@@ -73,9 +73,9 @@ internal fun AutoDiscoveryStatusView(
@Preview(showBackground = true)
@Composable
-internal fun AutoDiscoveryStatusViewTrustedPreview() {
+internal fun AutoDiscoveryResultViewTrustedPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusView(
+ AutoDiscoveryResultView(
settings = AutoDiscoveryResult.Settings(
incomingServerSettings = ImapServerSettings(
hostname = "imap.example.com".toHostname(),
@@ -101,9 +101,9 @@ internal fun AutoDiscoveryStatusViewTrustedPreview() {
@Preview(showBackground = true)
@Composable
-internal fun AutoDiscoveryStatusViewUntrustedPreview() {
+internal fun AutoDiscoveryResultViewUntrustedPreview() {
PreviewWithThemes {
- AutoDiscoveryStatusView(
+ AutoDiscoveryResultView(
settings = AutoDiscoveryResult.Settings(
incomingServerSettings = ImapServerSettings(
hostname = "imap.example.com".toHostname(),
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryServerSettingsView.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryServerSettingsView.kt
index d20b0748fb4040567fe9064b01fcd7cef316a683..9a86dc34d1818e64692f425c463986e45342e92f 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryServerSettingsView.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryServerSettingsView.kt
@@ -27,7 +27,6 @@ import app.k9mail.core.ui.compose.theme.Icons
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
import app.k9mail.feature.account.setup.ui.autodiscovery.toResourceString
-import app.k9mail.feature.account.setup.ui.common.toResourceString
@Composable
internal fun AutoDiscoveryServerSettingsView(
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusHeaderState.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusHeaderState.kt
deleted file mode 100644
index de36f44fcc2d36e64280c2fd5121fb49431f5615..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/view/AutoDiscoveryStatusHeaderState.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package app.k9mail.feature.account.setup.ui.autodiscovery.view
-
-import androidx.annotation.StringRes
-import androidx.compose.ui.graphics.vector.ImageVector
-import app.k9mail.core.ui.compose.theme.Icons
-import app.k9mail.feature.account.setup.R
-
-enum class AutoDiscoveryStatusHeaderState(
- val icon: ImageVector,
- @StringRes val titleResourceId: Int,
- @StringRes val subtitleResourceId: Int,
- val isExpandable: Boolean,
-) {
- NoSettings(
- icon = Icons.Outlined.info,
- titleResourceId = R.string.account_setup_auto_config_status_header_title_configuration_not_found,
- subtitleResourceId = R.string.account_setup_auto_config_status_header_subtitle_configuration_not_found,
- isExpandable = false,
- ),
-
- Trusted(
- icon = Icons.Outlined.check,
- titleResourceId = R.string.account_setup_auto_config_status_header_title_configuration_found,
- subtitleResourceId = R.string.account_setup_auto_config_status_header_subtitle_configuration_trusted,
- isExpandable = true,
- ),
-
- Untrusted(
- icon = Icons.Outlined.info,
- titleResourceId = R.string.account_setup_auto_config_status_header_title_configuration_found,
- subtitleResourceId = R.string.account_setup_auto_config_status_header_subtitle_configuration_untrusted,
- isExpandable = true,
- ),
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/AccountSetupStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/AccountSetupStringMapper.kt
deleted file mode 100644
index 15b40d767ac552ea291c8c26232fdc159d1db753..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/AccountSetupStringMapper.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package app.k9mail.feature.account.setup.ui.common
-
-import android.content.res.Resources
-import app.k9mail.core.common.domain.usecase.validation.ValidationError
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.domain.entity.ConnectionSecurity
-import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailAddress.ValidateEmailAddressError
-import app.k9mail.feature.account.setup.domain.usecase.ValidateImapPrefix.ValidateImapPrefixError
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePassword.ValidatePasswordError
-import app.k9mail.feature.account.setup.domain.usecase.ValidatePort.ValidatePortError
-import app.k9mail.feature.account.setup.domain.usecase.ValidateServer.ValidateServerError
-import app.k9mail.feature.account.setup.domain.usecase.ValidateUsername.ValidateUsernameError
-
-internal fun ConnectionSecurity.toResourceString(resources: Resources): String {
- return when (this) {
- ConnectionSecurity.None -> resources.getString(R.string.account_setup_connection_security_none)
- ConnectionSecurity.StartTLS -> resources.getString(R.string.account_setup_connection_security_start_tls)
- ConnectionSecurity.TLS -> resources.getString(R.string.account_setup_connection_security_ssl)
- }
-}
-
-internal fun ValidationError.toResourceString(resources: Resources): String {
- return when (this) {
- is ValidateEmailAddressError -> toEmailAddressErrorString(resources)
- is ValidateServerError -> toServerErrorString(resources)
- is ValidatePortError -> toPortErrorString(resources)
- is ValidateUsernameError -> toUsernameErrorString(resources)
- is ValidatePasswordError -> toPasswordErrorString(resources)
- is ValidateImapPrefixError -> toImapPrefixErrorString(resources)
- else -> throw IllegalArgumentException("Unknown error: $this")
- }
-}
-
-private fun ValidateEmailAddressError.toEmailAddressErrorString(resources: Resources): String {
- return when (this) {
- is ValidateEmailAddressError.EmptyEmailAddress -> resources.getString(
- R.string.account_setup_validation_error_email_address_required,
- )
-
- is ValidateEmailAddressError.InvalidEmailAddress -> resources.getString(
- R.string.account_setup_validation_error_email_address_invalid,
- )
- }
-}
-
-private fun ValidateServerError.toServerErrorString(resources: Resources): String {
- return when (this) {
- is ValidateServerError.EmptyServer -> resources.getString(
- R.string.account_setup_validation_error_server_required,
- )
- }
-}
-
-private fun ValidatePortError.toPortErrorString(resources: Resources): String {
- return when (this) {
- is ValidatePortError.EmptyPort -> resources.getString(
- R.string.account_setup_validation_error_port_required,
- )
-
- is ValidatePortError.InvalidPort -> resources.getString(
- R.string.account_setup_validation_error_port_invalid,
- )
- }
-}
-
-private fun ValidateUsernameError.toUsernameErrorString(resources: Resources): String {
- return when (this) {
- ValidateUsernameError.EmptyUsername -> resources.getString(
- R.string.account_setup_validation_error_username_required,
- )
- }
-}
-
-private fun ValidatePasswordError.toPasswordErrorString(resources: Resources): String {
- return when (this) {
- ValidatePasswordError.EmptyPassword -> resources.getString(
- R.string.account_setup_validation_error_password_required,
- )
- }
-}
-
-private fun ValidateImapPrefixError.toImapPrefixErrorString(resources: Resources): String {
- return when (this) {
- ValidateImapPrefixError.BlankImapPrefix -> resources.getString(
- R.string.account_setup_validation_error_imap_prefix_blank,
- )
- }
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/mapper/AuthenticationTypeStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/mapper/AuthenticationTypeStringMapper.kt
deleted file mode 100644
index cc6e28ce9cc78dc8bdf8c35239d907f922a75ae4..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/common/mapper/AuthenticationTypeStringMapper.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package app.k9mail.feature.account.setup.ui.common.mapper
-
-import android.content.res.Resources
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-
-internal fun AuthenticationType.toResourceString(resources: Resources): String {
- return when (this) {
- AuthenticationType.None -> {
- resources.getString(R.string.account_setup_authentication_none)
- }
- AuthenticationType.PasswordCleartext -> {
- resources.getString(R.string.account_setup_authentication_password_cleartext)
- }
- AuthenticationType.PasswordEncrypted -> {
- resources.getString(R.string.account_setup_authentication_password_encrypted)
- }
- AuthenticationType.ClientCertificate -> {
- resources.getString(R.string.account_setup_authentication_client_certificate)
- }
- AuthenticationType.OAuth2 -> {
- resources.getString(R.string.account_setup_authentication_client_oauth)
- }
- }
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateExtensions.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateExtensions.kt
deleted file mode 100644
index 9956324bf8ae5f20afabf8c994f29a85c72e2ca3..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/incoming/AccountIncomingConfigStateExtensions.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package app.k9mail.feature.account.setup.ui.incoming
-
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType.ClientCertificate
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType.OAuth2
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType.PasswordCleartext
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType.PasswordEncrypted
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.toImmutableList
-
-internal val AccountIncomingConfigContract.State.isPasswordFieldVisible: Boolean
- get() = authenticationType.isPasswordRequired
-
-internal val AccountIncomingConfigContract.State.allowedAuthenticationTypes: ImmutableList
- get() = protocolType.allowedAuthenticationTypes.toImmutableList()
-
-internal val IncomingProtocolType.allowedAuthenticationTypes: List
- get() = when (this) {
- IncomingProtocolType.IMAP -> {
- listOf(
- PasswordCleartext,
- PasswordEncrypted,
- ClientCertificate,
- OAuth2,
- )
- }
-
- IncomingProtocolType.POP3 -> {
- listOf(
- PasswordCleartext,
- PasswordEncrypted,
- ClientCertificate,
- )
- }
- }
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt
index 4472a6ed53edf4ff92e738ad141bf9b98ad22657..0b536d7110a7201a7f3e515d99e7c966f3ee035d 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt
@@ -2,9 +2,9 @@ package app.k9mail.feature.account.setup.ui.options
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency
import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount
-import app.k9mail.feature.account.setup.domain.input.StringInputField
interface AccountOptionsContract {
@@ -27,7 +27,7 @@ interface AccountOptionsContract {
data class OnMessageDisplayCountChanged(val messageDisplayCount: EmailDisplayCount) : Event
data class OnShowNotificationChanged(val showNotification: Boolean) : Event
- object LoadAccountSetupState : Event
+ object LoadAccountState : Event
object OnNextClicked : Event
object OnBackClicked : Event
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt
index 0af78ec6250332056a4b334e55bd667fe07429af..edd2fd31fc1f17c340fc2228fae6a46b9d392971 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt
@@ -10,13 +10,13 @@ import app.k9mail.core.ui.compose.common.mvi.observe
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.common.ui.AccountTopAppBar
import app.k9mail.feature.account.common.ui.WizardNavigationBar
+import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository
import app.k9mail.feature.account.setup.R.string
-import app.k9mail.feature.account.setup.ui.common.AccountSetupTopAppBar
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.ViewModel
-import app.k9mail.feature.account.setup.ui.preview.PreviewAccountSetupStateRepository
@Composable
internal fun AccountOptionsScreen(
@@ -33,7 +33,7 @@ internal fun AccountOptionsScreen(
}
LaunchedEffect(key1 = Unit) {
- dispatch(Event.LoadAccountSetupState)
+ dispatch(Event.LoadAccountState)
}
BackHandler {
@@ -42,14 +42,13 @@ internal fun AccountOptionsScreen(
Scaffold(
topBar = {
- AccountSetupTopAppBar(
+ AccountTopAppBar(
title = stringResource(id = string.account_setup_options_top_bar_title),
)
},
bottomBar = {
WizardNavigationBar(
nextButtonText = stringResource(id = string.account_setup_button_finish),
- backButtonText = stringResource(id = string.account_setup_button_back),
onNextClick = { dispatch(Event.OnNextClicked) },
onBackClick = { dispatch(Event.OnBackClicked) },
)
@@ -73,7 +72,7 @@ internal fun AccountOptionsScreenK9Preview() {
onBack = {},
viewModel = AccountOptionsViewModel(
validator = AccountOptionsValidator(),
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ accountStateRepository = PreviewAccountStateRepository(),
),
)
}
@@ -88,7 +87,7 @@ internal fun AccountOptionsScreenThunderbirdPreview() {
onBack = {},
viewModel = AccountOptionsViewModel(
validator = AccountOptionsValidator(),
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
+ accountStateRepository = PreviewAccountStateRepository(),
),
)
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt
index 58d0c88b203e73f85ff58919c83ffca6cab855ee..3d4c503022f1b2878c04459efb7c7d7f1b7ba1fc 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt
@@ -1,13 +1,13 @@
package app.k9mail.feature.account.setup.ui.options
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency
import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount
-import app.k9mail.feature.account.setup.domain.input.StringInputField
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State
-internal fun AccountSetupState.toAccountOptionsState(): State {
+internal fun AccountState.toAccountOptionsState(): State {
val options = options
return if (options == null) {
State(
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt
index 02e2901c54e5a9d93bfdfb1ef2acecadaca7a703..e255e17db36c0519ec04de9f9f0674f50c1840da 100644
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt
+++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt
@@ -2,7 +2,7 @@ package app.k9mail.feature.account.setup.ui.options
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.setup.domain.DomainContract
+import app.k9mail.feature.account.common.domain.AccountDomainContract
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State
@@ -11,16 +11,16 @@ import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.ViewMo
internal class AccountOptionsViewModel(
private val validator: Validator,
- private val accountSetupStateRepository: DomainContract.AccountSetupStateRepository,
+ private val accountStateRepository: AccountDomainContract.AccountStateRepository,
initialState: State? = null,
) : BaseViewModel(
- initialState = initialState ?: accountSetupStateRepository.getState().toAccountOptionsState(),
+ initialState = initialState ?: accountStateRepository.getState().toAccountOptionsState(),
),
ViewModel {
override fun event(event: Event) {
when (event) {
- Event.LoadAccountSetupState -> loadAccountSetupState()
+ Event.LoadAccountState -> loadAccountState()
is Event.OnAccountNameChanged -> updateState { state ->
state.copy(
@@ -63,9 +63,9 @@ internal class AccountOptionsViewModel(
}
}
- private fun loadAccountSetupState() {
+ private fun loadAccountState() {
updateState {
- accountSetupStateRepository.getState().toAccountOptionsState()
+ accountStateRepository.getState().toAccountOptionsState()
}
}
@@ -89,7 +89,7 @@ internal class AccountOptionsViewModel(
}
if (!hasError) {
- accountSetupStateRepository.saveOptions(state.value.toAccountOptions())
+ accountStateRepository.setOptions(state.value.toAccountOptions())
navigateNext()
}
}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateExtensions.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateExtensions.kt
deleted file mode 100644
index d80ffca57860c16a999c03683ead7443f1fad4c8..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/outgoing/AccountOutgoingConfigStateExtensions.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package app.k9mail.feature.account.setup.ui.outgoing
-
-internal val AccountOutgoingConfigContract.State.isUsernameFieldVisible: Boolean
- get() = authenticationType.isUsernameRequired
-
-internal val AccountOutgoingConfigContract.State.isPasswordFieldVisible: Boolean
- get() = authenticationType.isPasswordRequired
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/preview/PreviewAccountSetupStateRepository.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/preview/PreviewAccountSetupStateRepository.kt
deleted file mode 100644
index bc3aabb081c3e427ff41ea82c5fedde3d5a889d2..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/preview/PreviewAccountSetupStateRepository.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package app.k9mail.feature.account.setup.ui.preview
-
-import app.k9mail.feature.account.oauth.domain.entity.AuthorizationState
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
-import com.fsck.k9.mail.AuthType
-import com.fsck.k9.mail.ServerSettings
-
-class PreviewAccountSetupStateRepository : DomainContract.AccountSetupStateRepository {
- override fun getState(): AccountSetupState = AccountSetupState(
- emailAddress = "test@example.com",
- incomingServerSettings = ServerSettings(
- type = "imap",
- host = "imap.example.com",
- port = 993,
- connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
- authenticationType = AuthType.PLAIN,
- username = "test",
- password = "password",
- clientCertificateAlias = null,
- ),
- outgoingServerSettings = ServerSettings(
- type = "smtp",
- host = "smtp.example.com",
- port = 465,
- connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
- authenticationType = AuthType.PLAIN,
- username = "test",
- password = "password",
- clientCertificateAlias = null,
- ),
- )
-
- override fun save(accountSetupState: AccountSetupState) = Unit
-
- override fun saveEmailAddress(emailAddress: String) = Unit
-
- override fun saveIncomingServerSettings(serverSettings: ServerSettings) = Unit
-
- override fun saveOutgoingServerSettings(serverSettings: ServerSettings) = Unit
-
- override fun saveAuthorizationState(authorizationState: AuthorizationState) = Unit
-
- override fun saveOptions(options: AccountOptions) = Unit
-
- override fun clear() = Unit
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationMainScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationMainScreen.kt
deleted file mode 100644
index 16709f10b4bea397aac86c5cd524558cefcb3fac..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationMainScreen.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-package app.k9mail.feature.account.setup.ui.validation
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import app.k9mail.core.ui.compose.common.DevicePreviews
-import app.k9mail.core.ui.compose.designsystem.template.Scaffold
-import app.k9mail.core.ui.compose.theme.K9Theme
-import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
-import app.k9mail.feature.account.common.ui.AppTitleTopHeader
-import app.k9mail.feature.account.common.ui.WizardNavigationBar
-import app.k9mail.feature.account.common.ui.WizardNavigationBarState
-import app.k9mail.feature.account.oauth.ui.preview.PreviewAccountOAuthViewModel
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.data.InMemoryCertificateErrorRepository
-import app.k9mail.feature.account.setup.ui.preview.PreviewAccountSetupStateRepository
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Event
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.ViewModel
-import com.fsck.k9.mail.server.ServerSettingsValidationResult
-
-@Composable
-internal fun AccountValidationMainScreen(
- viewModel: ViewModel,
- modifier: Modifier = Modifier,
-) {
- val state = viewModel.state.collectAsStateWithLifecycle()
- val dispatch = { event: Event -> viewModel.event(event) }
-
- Scaffold(
- topBar = {
- AppTitleTopHeader(title = stringResource(id = R.string.account_setup_title))
- },
- bottomBar = {
- WizardNavigationBar(
- nextButtonText = "",
- backButtonText = stringResource(id = R.string.account_setup_button_back),
- onNextClick = {},
- onBackClick = { dispatch(Event.OnBackClicked) },
- state = WizardNavigationBarState(
- showNext = false,
- ),
- )
- },
- modifier = modifier,
- ) { innerPadding ->
- AccountValidationContent(
- onEvent = { dispatch(it) },
- state = state.value,
- isIncomingValidation = viewModel.isIncomingValidation,
- oAuthViewModel = viewModel.oAuthViewModel,
- contentPadding = innerPadding,
- )
- }
-}
-
-@Composable
-@DevicePreviews
-internal fun AccountIncomingValidationScreenK9Preview() {
- K9Theme {
- AccountValidationMainScreen(
- viewModel = AccountValidationViewModel(
- validateServerSettings = {
- ServerSettingsValidationResult.Success
- },
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
- authorizationStateRepository = { true },
- certificateErrorRepository = InMemoryCertificateErrorRepository(),
- oAuthViewModel = PreviewAccountOAuthViewModel(),
- isIncomingValidation = true,
- ),
- )
- }
-}
-
-@Composable
-@DevicePreviews
-internal fun AccountIncomingValidationScreenThunderbirdPreview() {
- ThunderbirdTheme {
- AccountValidationMainScreen(
- viewModel = AccountValidationViewModel(
- validateServerSettings = {
- ServerSettingsValidationResult.Success
- },
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
- authorizationStateRepository = { true },
- certificateErrorRepository = InMemoryCertificateErrorRepository(),
- oAuthViewModel = PreviewAccountOAuthViewModel(),
- isIncomingValidation = true,
- ),
- )
- }
-}
-
-@Composable
-@DevicePreviews
-internal fun AccountOutgoingValidationScreenK9Preview() {
- K9Theme {
- AccountValidationMainScreen(
- viewModel = AccountValidationViewModel(
- validateServerSettings = {
- ServerSettingsValidationResult.Success
- },
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
- authorizationStateRepository = { true },
- certificateErrorRepository = InMemoryCertificateErrorRepository(),
- oAuthViewModel = PreviewAccountOAuthViewModel(),
- isIncomingValidation = false,
- ),
- )
- }
-}
-
-@Composable
-@DevicePreviews
-internal fun AccountOutgoingValidationScreenThunderbirdPreview() {
- ThunderbirdTheme {
- AccountValidationMainScreen(
- viewModel = AccountValidationViewModel(
- validateServerSettings = {
- ServerSettingsValidationResult.Success
- },
- accountSetupStateRepository = PreviewAccountSetupStateRepository(),
- authorizationStateRepository = { true },
- certificateErrorRepository = InMemoryCertificateErrorRepository(),
- oAuthViewModel = PreviewAccountOAuthViewModel(),
- isIncomingValidation = false,
- ),
- )
- }
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStateMapper.kt
deleted file mode 100644
index 3087c7f0153c97926aa37bbb3c8729e55f6b8862..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStateMapper.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package app.k9mail.feature.account.setup.ui.validation
-
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-
-internal fun AccountSetupState.toValidationState(isIncomingValidation: Boolean): AccountValidationContract.State {
- return AccountValidationContract.State(
- emailAddress = emailAddress,
- serverSettings = if (isIncomingValidation) incomingServerSettings else outgoingServerSettings,
- isLoading = false,
- isSuccess = false,
- error = null,
- )
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStringMapper.kt
deleted file mode 100644
index 73a7e66ab68585a4270eaf5c5a8cea8acc4c1509..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/AccountValidationStringMapper.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package app.k9mail.feature.account.setup.ui.validation
-
-import android.content.res.Resources
-import app.k9mail.feature.account.setup.R
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract.Error
-
-internal fun Error.toResourceString(resources: Resources): String {
- return when (this) {
- is Error.AuthenticationError -> resources.getString(
- R.string.account_setup_settings_validation_error_authentication,
- )
-
- is Error.CertificateError -> resources.getString(
- R.string.account_setup_settings_validation_error_certificate,
- )
-
- is Error.NetworkError -> resources.getString(
- R.string.account_setup_settings_validation_error_network,
- )
-
- is Error.ServerError -> resources.getString(
- R.string.account_setup_settings_validation_error_server,
- )
-
- is Error.UnknownError -> resources.getString(
- R.string.account_setup_settings_validation_error_unknown,
- )
- }
-}
diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/InMemoryAuthStateStorage.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/InMemoryAuthStateStorage.kt
deleted file mode 100644
index b151b4ccacd4af103adedf247fa8c13fafd049ba..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/validation/InMemoryAuthStateStorage.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package app.k9mail.feature.account.setup.ui.validation
-
-import com.fsck.k9.mail.oauth.AuthStateStorage
-
-class InMemoryAuthStateStorage : AuthStateStorage {
- private var authorizationState: String? = null
-
- @Synchronized
- override fun getAuthorizationState(): String? {
- return authorizationState
- }
-
- @Synchronized
- override fun updateAuthorizationState(authorizationState: String?) {
- this.authorizationState = authorizationState
- }
-
- override fun getEmail(): String? {
- return null
- }
-}
diff --git a/feature/account/setup/src/main/res/values/strings.xml b/feature/account/setup/src/main/res/values/strings.xml
index 4546ad2c94b094ab4dc819cee85e629ed4c69b1c..557c9cd32626216d1bfce6fd1a47dfb06d2333d1 100644
--- a/feature/account/setup/src/main/res/values/strings.xml
+++ b/feature/account/setup/src/main/res/values/strings.xml
@@ -1,73 +1,27 @@
K-9 Mail
- Next
- Back
Finish
- None
- SSL/TLS
- StartTLS
- None
- Normal password
- Encrypted password
- Client certificate
- OAuth 2.0
- Client certificate
- None
Network
Unknown error
- Email address is required.
- Email address is invalid.
- Server name is required.
- Port is required.
- Port is invalid (must be 1–65535).
- Username is required.
- Password is required.
- Imap prefix can\'t be blank.
+ Email address is required.
+ Email address is invalid.
- Authentication error
- Certificate error
- Network error
- Server error
- Unknown error
- Checking incoming server settings…
- Checking incoming server settings failed!
- Incoming server settings are valid!
- Checking outgoing server settings…
- Checking outgoing server settings failed!
- Outgoing server settings are valid!
- Please sign in
-
- Finding email details
- Failed to load email configuration
- Configuration Found
- Configuration Not Found
- Configure automatically
- This configuration is not trusted
- We received the configuration for your email server over a connection that isn\'t as secure as we\'d like. This means that there is a tiny chance that someone could have altered it. Could you please double-check the provided configuration to make sure it\'s as it should be?
- Configure manually
- Edit configuration
- I trust this configuration
- It is required to approve the configuration.
-
- Incoming server settings
- Protocol
- Server
- Security
- Authentication
- Auto-detect IMAP namespace
- IMAP path prefix
- Use compression
- Send client ID
-
- Outgoing server settings
- Server
- Security
- Port
- Authentication
- Username
+ StartTLS
+ SSL/TLS
+ Finding email details
+ Failed to load email configuration
+ Configuration Found
+ Configuration Not Found
+ Configure automatically
+ This configuration is not trusted
+ We received the configuration for your email server over a connection that isn\'t as secure as we\'d like. This means that there is a tiny chance that someone could have altered it. Could you please double-check the provided configuration to make sure it\'s as it should be?
+ Configure manually
+ Edit configuration
+ I trust this configuration
+ It is required to approve the configuration.
Account options
Display options
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt
index c5552ff0f152226ad32e2e983e0ba4996d07c529..21acaf219444ceba9db58a5c0f1117ea295f9de5 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt
@@ -2,17 +2,18 @@ package app.k9mail.feature.account.setup
import android.content.Context
import app.k9mail.core.common.oauth.OAuthConfigurationFactory
+import app.k9mail.feature.account.common.AccountCommonExternalContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
+import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
+import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
import app.k9mail.feature.account.setup.ui.AccountSetupContract
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract
-import app.k9mail.feature.account.setup.ui.servercertificate.CertificateErrorContract
-import app.k9mail.feature.account.setup.ui.validation.AccountValidationContract
import com.fsck.k9.mail.oauth.AuthStateStorage
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
import com.fsck.k9.mail.oauth.OAuth2TokenProviderFactory
@@ -55,21 +56,23 @@ class AccountSetupModuleKtTest : KoinTest {
}
}
single { mock() }
+ single { mock() }
}
@Test
fun `should have a valid di module`() {
featureAccountSetupModule.verify(
extraTypes = listOf(
+ AccountCommonExternalContract.AccountStateLoader::class,
AccountSetupContract.State::class,
AccountAutoDiscoveryContract.State::class,
AccountOAuthContract.State::class,
- AccountValidationContract.State::class,
- AccountIncomingConfigContract.State::class,
- AccountOutgoingConfigContract.State::class,
+ ServerValidationContract.State::class,
+ IncomingServerSettingsContract.State::class,
+ OutgoingServerSettingsContract.State::class,
AccountOptionsContract.State::class,
- AccountSetupState::class,
- CertificateErrorContract.State::class,
+ AccountState::class,
+ ServerCertificateErrorContract.State::class,
AuthStateStorage::class,
Context::class,
Boolean::class,
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapperKtTest.kt
index 4a9f0cecfe8fb9a330f3a41557161aa258897a77..d254912ae2b4d1ebaea7a801fbb790991e2a5f89 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapperKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/AutoDiscoveryMapperKtTest.kt
@@ -8,7 +8,7 @@ import app.k9mail.autodiscovery.api.OutgoingServerSettings
import app.k9mail.autodiscovery.api.SmtpServerSettings
import app.k9mail.core.common.net.Hostname
import app.k9mail.core.common.net.Port
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationTypeKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationTypeKtTest.kt
index 9f71df260592b73625babe78f0e10aaec1fe44a1..47e0504332d9582466eb507a9150e752d43578a2 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationTypeKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryAuthenticationTypeKtTest.kt
@@ -1,5 +1,6 @@
package app.k9mail.feature.account.setup.domain.entity
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurityKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurityKtTest.kt
index 0901951aab584c14099e25ad8aa345c12f6ea6d3..31d112a20243768157a0fb85712122b8ee1ec35d 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurityKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/AutoDiscoveryConnectionSecurityKtTest.kt
@@ -1,5 +1,6 @@
package app.k9mail.feature.account.setup.domain.entity
+import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtensionKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtensionKtTest.kt
index 89206a0f2cf8cd9081e670d5d2cbb684aa94439b..66585646bcb629e59f0a35a7028409d9a88b5ddb 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtensionKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/entity/IncomingServerSettingsExtensionKtTest.kt
@@ -4,6 +4,7 @@ import app.k9mail.autodiscovery.api.AuthenticationType
import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.core.common.net.toHostname
import app.k9mail.core.common.net.toPort
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt
index f6a53976b79cac5e3fc68aad8e2073df8be6523a..eac495081d1aa848adfa35252061bf85485d09e8 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt
@@ -1,9 +1,9 @@
package app.k9mail.feature.account.setup.domain.usecase
+import app.k9mail.feature.account.common.domain.entity.Account
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
-import app.k9mail.feature.account.setup.domain.entity.Account
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
@@ -21,6 +21,7 @@ class CreateAccountTest {
recordedAccount = account
AccountCreatorResult.Success(accountUuid = "uuid")
},
+ uuidGenerator = { "uuid" },
)
val emailAddress = "user@example.com"
@@ -65,6 +66,7 @@ class CreateAccountTest {
assertThat(result).isEqualTo("uuid")
assertThat(recordedAccount).isEqualTo(
Account(
+ uuid = "uuid",
emailAddress = emailAddress,
incomingServerSettings = incomingServerSettings,
outgoingServerSettings = outgoingServerSettings,
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreenKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreenKtTest.kt
index 2bcbef2fed7ece622cefbc3e26345c20f78de6cc..3d1a16e828adb193acc118a73e17bc6b41c48e11 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreenKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupScreenKtTest.kt
@@ -4,14 +4,14 @@ import app.k9mail.core.ui.compose.testing.ComposeTest
import app.k9mail.core.ui.compose.testing.onNodeWithTag
import app.k9mail.core.ui.compose.testing.setContent
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
+import app.k9mail.feature.account.server.settings.ui.incoming.fake.FakeIncomingServerSettingsViewModel
+import app.k9mail.feature.account.server.settings.ui.outgoing.fake.FakeOutgoingServerSettingsViewModel
+import app.k9mail.feature.account.server.validation.ui.fake.FakeServerValidationViewModel
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
import app.k9mail.feature.account.setup.ui.AccountSetupContract.SetupStep
import app.k9mail.feature.account.setup.ui.AccountSetupContract.State
import app.k9mail.feature.account.setup.ui.autodiscovery.FakeAccountAutoDiscoveryViewModel
-import app.k9mail.feature.account.setup.ui.incoming.FakeAccountIncomingConfigViewModel
import app.k9mail.feature.account.setup.ui.options.FakeAccountOptionsViewModel
-import app.k9mail.feature.account.setup.ui.outgoing.FakeAccountOutgoingConfigViewModel
-import app.k9mail.feature.account.setup.ui.validation.FakeAccountValidationViewModel
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.test.runTest
@@ -30,10 +30,10 @@ class AccountSetupScreenKtTest : ComposeTest() {
onBack = { },
viewModel = viewModel,
autoDiscoveryViewModel = FakeAccountAutoDiscoveryViewModel(),
- incomingViewModel = FakeAccountIncomingConfigViewModel(),
- incomingValidationViewModel = FakeAccountValidationViewModel(),
- outgoingViewModel = FakeAccountOutgoingConfigViewModel(),
- outgoingValidationViewModel = FakeAccountValidationViewModel(),
+ incomingViewModel = FakeIncomingServerSettingsViewModel(),
+ incomingValidationViewModel = FakeServerValidationViewModel(),
+ outgoingViewModel = FakeOutgoingServerSettingsViewModel(),
+ outgoingValidationViewModel = FakeServerValidationViewModel(),
optionsViewModel = FakeAccountOptionsViewModel(),
)
}
@@ -59,10 +59,10 @@ class AccountSetupScreenKtTest : ComposeTest() {
onBack = { onBackCounter++ },
viewModel = viewModel,
autoDiscoveryViewModel = FakeAccountAutoDiscoveryViewModel(),
- incomingViewModel = FakeAccountIncomingConfigViewModel(),
- incomingValidationViewModel = FakeAccountValidationViewModel(),
- outgoingViewModel = FakeAccountOutgoingConfigViewModel(),
- outgoingValidationViewModel = FakeAccountValidationViewModel(),
+ incomingViewModel = FakeIncomingServerSettingsViewModel(),
+ incomingValidationViewModel = FakeServerValidationViewModel(),
+ outgoingViewModel = FakeOutgoingServerSettingsViewModel(),
+ outgoingValidationViewModel = FakeServerValidationViewModel(),
optionsViewModel = FakeAccountOptionsViewModel(),
)
}
@@ -84,9 +84,9 @@ class AccountSetupScreenKtTest : ComposeTest() {
private fun getTagForStep(step: SetupStep): String = when (step) {
SetupStep.AUTO_CONFIG -> "AccountAutoDiscoveryContent"
- SetupStep.INCOMING_CONFIG -> "AccountIncomingConfigContent"
+ SetupStep.INCOMING_CONFIG -> "IncomingServerSettingsContent"
SetupStep.INCOMING_VALIDATION -> "AccountValidationContent"
- SetupStep.OUTGOING_CONFIG -> "AccountOutgoingConfigContent"
+ SetupStep.OUTGOING_CONFIG -> "OutgoingServerSettingsContent"
SetupStep.OUTGOING_VALIDATION -> "AccountValidationContent"
SetupStep.OPTIONS -> "AccountOptionsContent"
}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModelTest.kt
index c9937c5fef49ef8f056a6138f9bc6ce0dc877834..db4d38547c8e6ad6af8ad63f8dc996be61fdb353 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModelTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/AccountSetupViewModelTest.kt
@@ -3,10 +3,10 @@ package app.k9mail.feature.account.setup.ui
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.MailConnectionSecurity
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
import app.k9mail.feature.account.setup.ui.AccountSetupContract.SetupStep
import app.k9mail.feature.account.setup.ui.AccountSetupContract.State
@@ -33,7 +33,7 @@ class AccountSetupViewModelTest {
var createAccountOutgoingServerSettings: ServerSettings? = null
var createAccountAuthorizationState: String? = null
var createAccountOptions: AccountOptions? = null
- val accountSetupStateRepository = InMemoryAccountSetupStateRepository()
+ val accountStateRepository = InMemoryAccountStateRepository()
val viewModel = AccountSetupViewModel(
createAccount = { emailAddress, incomingServerSettings, outgoingServerSettings, authState, options ->
createAccountEmailAddress = emailAddress
@@ -44,7 +44,7 @@ class AccountSetupViewModelTest {
"accountUuid"
},
- accountSetupStateRepository = accountSetupStateRepository,
+ accountStateRepository = accountStateRepository,
)
val turbines = turbinesWithInitialStateCheck(viewModel, State(setupStep = SetupStep.AUTO_CONFIG))
@@ -54,7 +54,7 @@ class AccountSetupViewModelTest {
),
)
- val expectedAccountSetupState = AccountSetupState(
+ val expectedAccountState = AccountState(
emailAddress = "test@domain.example",
incomingServerSettings = ServerSettings(
type = "imap",
@@ -132,7 +132,7 @@ class AccountSetupViewModelTest {
prop(State::setupStep).isEqualTo(SetupStep.OPTIONS)
}
- accountSetupStateRepository.save(expectedAccountSetupState)
+ accountStateRepository.setState(expectedAccountState)
viewModel.event(AccountSetupContract.Event.OnNext)
@@ -144,10 +144,10 @@ class AccountSetupViewModelTest {
}
assertThat(createAccountEmailAddress).isEqualTo(EMAIL_ADDRESS)
- assertThat(createAccountIncomingServerSettings).isEqualTo(expectedAccountSetupState.incomingServerSettings)
- assertThat(createAccountOutgoingServerSettings).isEqualTo(expectedAccountSetupState.outgoingServerSettings)
+ assertThat(createAccountIncomingServerSettings).isEqualTo(expectedAccountState.incomingServerSettings)
+ assertThat(createAccountOutgoingServerSettings).isEqualTo(expectedAccountState.outgoingServerSettings)
assertThat(createAccountAuthorizationState).isNull()
- assertThat(createAccountOptions).isEqualTo(expectedAccountSetupState.options)
+ assertThat(createAccountOptions).isEqualTo(expectedAccountState.options)
}
@Test
@@ -155,7 +155,7 @@ class AccountSetupViewModelTest {
val initialState = State(setupStep = SetupStep.OPTIONS)
val viewModel = AccountSetupViewModel(
createAccount = { _, _, _, _, _ -> "accountUuid" },
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(viewModel, initialState)
@@ -205,7 +205,7 @@ class AccountSetupViewModelTest {
)
val viewModel = AccountSetupViewModel(
createAccount = { _, _, _, _, _ -> "accountUuid" },
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(viewModel, initialState)
@@ -237,7 +237,7 @@ class AccountSetupViewModelTest {
)
val viewModel = AccountSetupViewModel(
createAccount = { _, _, _, _, _ -> "accountUuid" },
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(viewModel, initialState)
@@ -269,7 +269,7 @@ class AccountSetupViewModelTest {
)
val viewModel = AccountSetupViewModel(
createAccount = { _, _, _, _, _ -> "accountUuid" },
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(viewModel, initialState)
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt
index 6615b5019bf62858c2e2ae49d1f4855978839876..802cfe0e66eebf91fc195f57f3e356b7a26bfe08 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt
@@ -5,17 +5,17 @@ import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.autodiscovery.api.SmtpServerSettings
import app.k9mail.core.common.net.toHostname
import app.k9mail.core.common.net.toPort
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
-import app.k9mail.feature.account.setup.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.entity.AuthenticationType
+import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
+import app.k9mail.feature.account.common.domain.input.NumberInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
+import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryAuthenticationType
import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryConnectionSecurity
-import app.k9mail.feature.account.setup.domain.entity.IncomingProtocolType
import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity
-import app.k9mail.feature.account.setup.domain.input.NumberInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
@@ -23,11 +23,11 @@ import org.junit.Test
class AccountAutoDiscoveryStateMapperKtTest {
@Test
- fun `should map to empty AccountSetupState when empty`() {
- val accountSetupState = EMPTY_STATE.toAccountSetupState()
+ fun `should map to empty AccountState when empty`() {
+ val accountState = EMPTY_STATE.toAccountState()
- assertThat(accountSetupState).isEqualTo(
- AccountSetupState(
+ assertThat(accountState).isEqualTo(
+ AccountState(
emailAddress = "",
incomingServerSettings = null,
outgoingServerSettings = null,
@@ -41,7 +41,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
fun `should map to default IncomingConfigState when empty`() {
val incomingConfigState = EMPTY_STATE.toIncomingConfigState()
- assertThat(incomingConfigState).isEqualTo(AccountIncomingConfigContract.State())
+ assertThat(incomingConfigState).isEqualTo(IncomingServerSettingsContract.State())
}
@Test
@@ -49,7 +49,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
val incomingConfigState = EMAIL_PASSWORD_STATE.toIncomingConfigState()
assertThat(incomingConfigState).isEqualTo(
- AccountIncomingConfigContract.State(
+ IncomingServerSettingsContract.State(
username = StringInputField(value = EMAIL_ADDRESS),
password = StringInputField(value = PASSWORD),
),
@@ -61,7 +61,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
val incomingConfigState = AUTO_DISCOVERY_STATE.toIncomingConfigState()
assertThat(incomingConfigState).isEqualTo(
- AccountIncomingConfigContract.State(
+ IncomingServerSettingsContract.State(
protocolType = IncomingProtocolType.IMAP,
server = StringInputField(value = AUTO_DISCOVERY_HOSTNAME.value),
security = AUTO_DISCOVERY_SECURITY.toConnectionSecurity(),
@@ -78,7 +78,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
val incomingConfigState = AUTO_DISCOVERY_STATE_USERNAME_EMPTY.toIncomingConfigState()
assertThat(incomingConfigState).isEqualTo(
- AccountIncomingConfigContract.State(
+ IncomingServerSettingsContract.State(
protocolType = IncomingProtocolType.IMAP,
server = StringInputField(value = AUTO_DISCOVERY_HOSTNAME.value),
security = AUTO_DISCOVERY_SECURITY.toConnectionSecurity(),
@@ -94,7 +94,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
fun `should map to OutgoingConfigState when empty`() {
val outgoingConfigState = EMPTY_STATE.toOutgoingConfigState()
- assertThat(outgoingConfigState).isEqualTo(AccountOutgoingConfigContract.State())
+ assertThat(outgoingConfigState).isEqualTo(OutgoingServerSettingsContract.State())
}
@Test
@@ -102,7 +102,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
val outgoingConfigState = EMAIL_PASSWORD_STATE.toOutgoingConfigState()
assertThat(outgoingConfigState).isEqualTo(
- AccountOutgoingConfigContract.State(
+ OutgoingServerSettingsContract.State(
username = StringInputField(value = EMAIL_ADDRESS),
password = StringInputField(value = PASSWORD),
),
@@ -114,7 +114,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
val outgoingConfigState = AUTO_DISCOVERY_STATE.toOutgoingConfigState()
assertThat(outgoingConfigState).isEqualTo(
- AccountOutgoingConfigContract.State(
+ OutgoingServerSettingsContract.State(
server = StringInputField(value = AUTO_DISCOVERY_HOSTNAME.value),
security = AUTO_DISCOVERY_SECURITY.toConnectionSecurity(),
port = NumberInputField(value = AUTO_DISCOVERY_PORT_SMTP.value.toLong()),
@@ -130,7 +130,7 @@ class AccountAutoDiscoveryStateMapperKtTest {
val outgoingConfigState = AUTO_DISCOVERY_STATE_USERNAME_EMPTY.toOutgoingConfigState()
assertThat(outgoingConfigState).isEqualTo(
- AccountOutgoingConfigContract.State(
+ OutgoingServerSettingsContract.State(
server = StringInputField(value = AUTO_DISCOVERY_HOSTNAME.value),
security = AUTO_DISCOVERY_SECURITY.toConnectionSecurity(),
port = NumberInputField(value = AUTO_DISCOVERY_PORT_SMTP.value.toLong()),
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateTest.kt
index fee85038cd369f422181fdf81559323116151ccb..8fcfff3eefd90d2613b12ad00b56daea1a6f98db 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateTest.kt
@@ -1,7 +1,7 @@
package app.k9mail.feature.account.setup.ui.autodiscovery
-import app.k9mail.feature.account.setup.domain.input.BooleanInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
+import app.k9mail.feature.account.common.domain.input.BooleanInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.ConfigStep
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.State
import assertk.assertThat
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt
index 7099eb6dce121510980a750298efd40a1402e462..25919b68faeb0dbe9d6fdc9051c9dd82bcdeaca3 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt
@@ -7,13 +7,13 @@ import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
-import app.k9mail.feature.account.setup.domain.DomainContract
-import app.k9mail.feature.account.setup.domain.entity.AccountSetupState
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.AccountDomainContract
+import app.k9mail.feature.account.common.domain.entity.AccountState
+import app.k9mail.feature.account.common.domain.input.BooleanInputField
+import app.k9mail.feature.account.common.domain.input.StringInputField
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
import app.k9mail.feature.account.setup.domain.entity.AutoDiscoverySettingsFixture
-import app.k9mail.feature.account.setup.domain.input.BooleanInputField
-import app.k9mail.feature.account.setup.domain.input.StringInputField
-import app.k9mail.feature.account.setup.ui.FakeAccountOAuthViewModel
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.ConfigStep
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Effect
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Error
@@ -67,11 +67,11 @@ class AccountAutoDiscoveryViewModelTest {
}
@Test
- fun `should change state when ConfigurationApprovalChanged event is received`() = runTest {
+ fun `should change state when ResultApprovalChanged event is received`() = runTest {
eventStateTest(
viewModel = createTestSubject(),
initialState = State(),
- event = Event.ConfigurationApprovalChanged(true),
+ event = Event.ResultApprovalChanged(true),
expectedState = State(
configurationApproved = BooleanInputField(value = true),
),
@@ -94,7 +94,7 @@ class AccountAutoDiscoveryViewModelTest {
autoDiscoverySettings
},
oAuthViewModel = FakeAccountOAuthViewModel(),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(testSubject, initialState)
@@ -143,7 +143,7 @@ class AccountAutoDiscoveryViewModelTest {
AutoDiscoveryResult.UnexpectedException(discoveryError)
},
oAuthViewModel = FakeAccountOAuthViewModel(),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(testSubject, initialState)
@@ -217,7 +217,7 @@ class AccountAutoDiscoveryViewModelTest {
),
getAutoDiscovery = { AutoDiscoveryResult.NoUsableSettingsFound },
oAuthViewModel = FakeAccountOAuthViewModel(),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
@@ -245,7 +245,7 @@ class AccountAutoDiscoveryViewModelTest {
emailAddress = StringInputField(value = "email"),
password = StringInputField(value = "password"),
)
- val repository = InMemoryAccountSetupStateRepository()
+ val repository = InMemoryAccountStateRepository()
val testSubject = createTestSubject(
initialState = initialState,
repository = repository,
@@ -283,7 +283,7 @@ class AccountAutoDiscoveryViewModelTest {
}
assertThat(repository.getState()).isEqualTo(
- AccountSetupState(
+ AccountState(
emailAddress = "email",
incomingServerSettings = null,
outgoingServerSettings = null,
@@ -307,7 +307,7 @@ class AccountAutoDiscoveryViewModelTest {
),
getAutoDiscovery = { AutoDiscoveryResult.NoUsableSettingsFound },
oAuthViewModel = FakeAccountOAuthViewModel(),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
initialState = initialState,
)
val turbines = turbinesWithInitialStateCheck(viewModel, initialState)
@@ -431,7 +431,7 @@ class AccountAutoDiscoveryViewModelTest {
private companion object {
fun createTestSubject(
initialState: State = State(),
- repository: DomainContract.AccountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ repository: AccountDomainContract.AccountStateRepository = InMemoryAccountStateRepository(),
): AccountAutoDiscoveryViewModel {
return AccountAutoDiscoveryViewModel(
validator = FakeAccountAutoDiscoveryValidator(),
@@ -439,7 +439,7 @@ class AccountAutoDiscoveryViewModelTest {
delay(50)
AutoDiscoveryResult.NoUsableSettingsFound
},
- accountSetupStateRepository = repository,
+ accountStateRepository = repository,
oAuthViewModel = FakeAccountOAuthViewModel(),
initialState = initialState,
)
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/FakeAccountAutoDiscoveryViewModel.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/FakeAccountAutoDiscoveryViewModel.kt
index a33c8373bd671d609089f9ad199689b526f18c98..c4d372ae21fdd62c0bea113d20a0e84fe41a5053 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/FakeAccountAutoDiscoveryViewModel.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/FakeAccountAutoDiscoveryViewModel.kt
@@ -2,7 +2,7 @@ package app.k9mail.feature.account.setup.ui.autodiscovery
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
-import app.k9mail.feature.account.setup.ui.FakeAccountOAuthViewModel
+import app.k9mail.feature.account.oauth.ui.fake.FakeAccountOAuthViewModel
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Effect
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.Event
import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract.State
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/FakeAccountIncomingConfigViewModel.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/FakeAccountIncomingConfigViewModel.kt
deleted file mode 100644
index 3fda4d2e7795830c3fe8956293839be133190bf9..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/incoming/FakeAccountIncomingConfigViewModel.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package app.k9mail.feature.account.setup.ui.incoming
-
-import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.State
-import app.k9mail.feature.account.setup.ui.incoming.AccountIncomingConfigContract.ViewModel
-
-class FakeAccountIncomingConfigViewModel(
- initialState: State = State(),
-) : BaseViewModel(initialState), ViewModel {
-
- val events = mutableListOf()
-
- override fun event(event: Event) {
- events.add(event)
- }
-
- fun effect(effect: Effect) {
- emitEffect(effect)
- }
-}
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt
index 4ef8082def54db9776b9065459c8384ac01b06a9..cfaa6c1e052b6dc995558edf6fb5cc69e7b3a7de 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt
@@ -1,9 +1,9 @@
package app.k9mail.feature.account.setup.ui.options
-import app.k9mail.feature.account.setup.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.entity.AccountOptions
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency
import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount
-import app.k9mail.feature.account.setup.domain.input.StringInputField
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isNull
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt
index eeb7ec44e4eebc88fd771a5f70211badba0ad895..40526d70134ec92143a15488a5786b16a4c1d5f8 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt
@@ -1,8 +1,8 @@
package app.k9mail.feature.account.setup.ui.options
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency
import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount
-import app.k9mail.feature.account.setup.domain.input.StringInputField
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State
import assertk.assertThat
import assertk.assertions.isEqualTo
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt
index a47689aca080d9d351fc7d224e7f7136781ef13e..f610c02494010d30c5bad6e1ec3d549b0663fbef 100644
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt
+++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt
@@ -5,22 +5,20 @@ import app.k9mail.core.common.domain.usecase.validation.ValidationError
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
-import app.k9mail.feature.account.setup.data.InMemoryAccountSetupStateRepository
+import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
+import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency
import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount
-import app.k9mail.feature.account.setup.domain.input.StringInputField
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State
import assertk.assertThat
import assertk.assertions.assertThatAndTurbinesConsumed
import assertk.assertions.isEqualTo
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalCoroutinesApi::class)
class AccountOptionsViewModelTest {
@get:Rule
@@ -28,7 +26,7 @@ class AccountOptionsViewModelTest {
private val testSubject = AccountOptionsViewModel(
validator = FakeAccountOptionsValidator(),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
)
@Test
@@ -137,7 +135,7 @@ class AccountOptionsViewModelTest {
validator = FakeAccountOptionsValidator(
accountNameAnswer = ValidationResult.Failure(TestError),
),
- accountSetupStateRepository = InMemoryAccountSetupStateRepository(),
+ accountStateRepository = InMemoryAccountStateRepository(),
)
val stateTurbine = viewModel.state.testIn(backgroundScope)
val effectTurbine = viewModel.effect.testIn(backgroundScope)
diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/FakeAccountOutgoingConfigViewModel.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/FakeAccountOutgoingConfigViewModel.kt
deleted file mode 100644
index 0c2838600c8a24bbf162167877e7d8986e0f2cc3..0000000000000000000000000000000000000000
--- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/outgoing/FakeAccountOutgoingConfigViewModel.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package app.k9mail.feature.account.setup.ui.outgoing
-
-import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Effect
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.Event
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.State
-import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigContract.ViewModel
-
-class FakeAccountOutgoingConfigViewModel(
- initialState: State = State(),
-) : BaseViewModel(initialState), ViewModel {
-
- val events = mutableListOf()
-
- override fun event(event: Event) {
- events.add(event)
- }
-
- fun effect(effect: Effect) {
- emitEffect(effect)
- }
-}
diff --git a/feature/launcher/build.gradle.kts b/feature/launcher/build.gradle.kts
index ed6f16361add6e2ee969d4722893ac866237f7f1..e28f2540e103f5dadfc0e0087cde4c4b1b865379 100644
--- a/feature/launcher/build.gradle.kts
+++ b/feature/launcher/build.gradle.kts
@@ -20,6 +20,7 @@ dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.feature.onboarding)
implementation(projects.feature.account.setup)
+ implementation(projects.feature.account.edit)
testImplementation(projects.core.ui.compose.testing)
}
diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt
index fe1be03d4cd558111b3084a4140fdf1bf873dd03..485bb83a68323debe1b9e89cf5c7a4409b8cc468 100644
--- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt
+++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/FeatureLauncherActivity.kt
@@ -6,6 +6,10 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.core.view.WindowCompat
import app.k9mail.core.ui.compose.common.activity.setActivityContent
+import app.k9mail.core.ui.compose.common.navigation.toDeepLinkUri
+import app.k9mail.feature.account.edit.navigation.NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_INCOMING
+import app.k9mail.feature.account.edit.navigation.NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_OUTGOING
+import app.k9mail.feature.account.edit.navigation.withAccountUuid
import app.k9mail.feature.account.setup.navigation.NAVIGATION_ROUTE_ACCOUNT_SETUP
import app.k9mail.feature.launcher.ui.FeatureLauncherApp
import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING
@@ -17,22 +21,16 @@ class FeatureLauncherActivity : ComponentActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
- val destination = intent.getStringExtra(EXTRA_DESTINATION)
-
setActivityContent {
- FeatureLauncherApp(startDestination = destination)
+ FeatureLauncherApp()
}
}
companion object {
- private const val EXTRA_DESTINATION = "destination"
- private const val DESTINATION_ONBOARDING = NAVIGATION_ROUTE_ONBOARDING
- private const val DESTINATION_SETUP_ACCOUNT = NAVIGATION_ROUTE_ACCOUNT_SETUP
-
@JvmStatic
fun launchOnboarding(context: Activity) {
val intent = Intent(context, FeatureLauncherActivity::class.java).apply {
- putExtra(EXTRA_DESTINATION, DESTINATION_ONBOARDING)
+ data = NAVIGATION_ROUTE_ONBOARDING.toDeepLinkUri()
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
context.startActivity(intent)
@@ -41,7 +39,23 @@ class FeatureLauncherActivity : ComponentActivity() {
@JvmStatic
fun launchSetupAccount(context: Activity) {
val intent = Intent(context, FeatureLauncherActivity::class.java).apply {
- putExtra(EXTRA_DESTINATION, DESTINATION_SETUP_ACCOUNT)
+ data = NAVIGATION_ROUTE_ACCOUNT_SETUP.toDeepLinkUri()
+ }
+ context.startActivity(intent)
+ }
+
+ @JvmStatic
+ fun launchEditIncomingSettings(context: Activity, accountUuid: String) {
+ val intent = Intent(context, FeatureLauncherActivity::class.java).apply {
+ data = NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_INCOMING.withAccountUuid(accountUuid).toDeepLinkUri()
+ }
+ context.startActivity(intent)
+ }
+
+ @JvmStatic
+ fun launchEditOutgoingSettings(context: Activity, accountUuid: String) {
+ val intent = Intent(context, FeatureLauncherActivity::class.java).apply {
+ data = NAVIGATION_ROUTE_ACCOUNT_EDIT_CONFIG_OUTGOING.withAccountUuid(accountUuid).toDeepLinkUri()
}
context.startActivity(intent)
}
diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt
index 7cc641f17b5f920819bf41616928239c4babdaf5..1e5a5e473555743b986928688673c81597702e01 100644
--- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt
+++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/di/FeatureLauncherModule.kt
@@ -1,10 +1,12 @@
package app.k9mail.feature.launcher.di
+import app.k9mail.feature.account.edit.featureAccountEditModule
import app.k9mail.feature.account.setup.featureAccountSetupModule
import org.koin.dsl.module
val featureLauncherModule = module {
includes(
featureAccountSetupModule,
+ featureAccountEditModule,
)
}
diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt
index a5b78510fe371947be86e9cc1ebf8353361528d1..5de11098ba7b8e43525e4ae9614bcc9cff2111f4 100644
--- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt
+++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/navigation/FeatureLauncherNavHost.kt
@@ -4,6 +4,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
+import app.k9mail.core.ui.compose.common.activity.LocalActivity
+import app.k9mail.feature.account.edit.navigation.accountEditRoute
import app.k9mail.feature.account.setup.navigation.accountSetupRoute
import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup
import app.k9mail.feature.launcher.FeatureLauncherExternalContract.AccountSetupFinishedLauncher
@@ -15,15 +17,16 @@ import org.koin.compose.koinInject
@Composable
fun FeatureLauncherNavHost(
navController: NavHostController,
- startDestination: String?,
onBack: () -> Unit,
modifier: Modifier = Modifier,
importSettingsLauncher: ImportSettingsLauncher = koinInject(),
accountSetupFinishedLauncher: AccountSetupFinishedLauncher = koinInject(),
) {
+ val activity = LocalActivity.current
+
NavHost(
navController = navController,
- startDestination = startDestination ?: NAVIGATION_ROUTE_ONBOARDING,
+ startDestination = NAVIGATION_ROUTE_ONBOARDING,
modifier = modifier,
) {
onboardingRoute(
@@ -34,5 +37,9 @@ fun FeatureLauncherNavHost(
onBack = onBack,
onFinish = { accountSetupFinishedLauncher.launch(it) },
)
+ accountEditRoute(
+ onBack = onBack,
+ onFinish = { activity.finish() },
+ )
}
}
diff --git a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/ui/FeatureLauncherApp.kt b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/ui/FeatureLauncherApp.kt
index 0bafc94ec02854092bbcefb7b0ca2a994f9c949b..27aef780a57608dc8d2c9a29498115326f898371 100644
--- a/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/ui/FeatureLauncherApp.kt
+++ b/feature/launcher/src/main/kotlin/app/k9mail/feature/launcher/ui/FeatureLauncherApp.kt
@@ -12,7 +12,6 @@ import app.k9mail.feature.launcher.navigation.FeatureLauncherNavHost
@Composable
fun FeatureLauncherApp(
- startDestination: String?,
modifier: Modifier = Modifier,
) {
val navController = rememberNavController()
@@ -28,7 +27,6 @@ fun FeatureLauncherApp(
FeatureLauncherNavHost(
navController = navController,
- startDestination = startDestination,
onBack = { activity.finish() },
)
}
diff --git a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt b/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt
index 86a700e1bb2e056224b87a43bb75dcf9d140b140..a9e2263afbb7e438c134a8dbd6a1af80e4eaef6c 100644
--- a/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt
+++ b/feature/onboarding/src/main/kotlin/app/k9mail/feature/onboarding/navigation/OnboardingNavigation.kt
@@ -3,10 +3,10 @@ package app.k9mail.feature.onboarding.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
-import androidx.navigation.compose.composable
+import app.k9mail.core.ui.compose.common.navigation.deepLinkComposable
import app.k9mail.feature.onboarding.ui.OnboardingScreen
-const val NAVIGATION_ROUTE_ONBOARDING = "/onboarding"
+const val NAVIGATION_ROUTE_ONBOARDING = "onboarding"
fun NavController.navigateToOnboarding(
navOptions: NavOptions? = null,
@@ -18,7 +18,7 @@ fun NavGraphBuilder.onboardingRoute(
onStart: () -> Unit,
onImport: () -> Unit,
) {
- composable(route = NAVIGATION_ROUTE_ONBOARDING) {
+ deepLinkComposable(route = NAVIGATION_ROUTE_ONBOARDING) {
OnboardingScreen(
onStartClick = onStart,
onImportClick = onImport,
diff --git a/gradle.properties b/gradle.properties
index 1840fe6ac2ae8e97347e35c09dee0b0076e6bb7e..bf3ff324c464aeea627d3413baca24090af1291d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,16 +1,24 @@
-# Disable buildFeatures flags by default
+# Android
+android.useAndroidX=true
+android.enableJetifier=false
+android.nonTransitiveRClass=true
+
+## Disable buildFeatures flags by default
android.defaults.buildfeatures.aidl=false
android.defaults.buildfeatures.buildconfig=false
android.defaults.buildfeatures.renderscript=false
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
-android.useAndroidX=true
-android.enableJetifier=true
-android.nonTransitiveRClass=true
+# Gradle
+## Ensure important default jvmargs aren't overwritten. See https://github.com/gradle/gradle/issues/19750
+org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError
-org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8
org.gradle.parallel=true
+
org.gradle.caching=true
android.enableR8.fullMode=false
+org.gradle.configuration-cache=true
+
+org.gradle.kotlin.dsl.allWarningsAsErrors=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 531b1cb6b601cef7bacb14972dab957a0c17c586..1a61eae84adfd24170713710b67735feba6d7b41 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,54 +2,56 @@
# Please don't open pull requests upgrading dependencies if you're a new contributor.
[versions]
-gradle = "8.1.1"
-androidGradlePlugin = "8.0.2"
-ktlint = "0.48.2"
+gradle = "8.3"
+androidGradlePlugin = "8.1.1"
+ktlint = "0.50.0"
-kotlin = "1.8.21"
-kotlinxCoroutines = "1.7.1"
+kotlin = "1.9.10"
+kotlinxCoroutines = "1.7.3"
jetbrainsAnnotations = "24.0.1"
androidxAppCompat = "1.6.1"
androidxActivity = "1.7.2"
-androidxRecyclerView = "1.3.0"
-androidxLifecycle = "2.6.1"
-androidxNavigation = "2.6.0"
+androidxRecyclerView = "1.3.1"
+androidxLifecycle = "2.6.2"
+androidxNavigation = "2.7.2"
androidxConstraintLayout = "2.1.4"
-androidxFragment = "1.6.0"
-androidxCore = "1.10.1"
+androidxFragment = "1.6.1"
+androidxCore = "1.12.0"
androidxCoreSplashscreen = "1.0.1"
-androidxPreference = "1.2.0"
+androidxPreference = "1.2.1"
androidxDrawerLayout = "1.1.1"
androidxTransition = "1.4.1"
-androidxComposeCompiler = "1.4.7"
-androidxComposeBom = "2023.05.01"
-androidxComposeMaterial = "1.4.3"
-accompanist = "0.30.1"
+androidxComposeCompiler = "1.5.3"
+# https://developer.android.com/jetpack/compose/bom/bom-mapping
+androidxComposeBom = "2023.09.00"
+androidxComposeMaterial = "1.5.1"
+androidxComposeMaterial3 = "1.1.1"
+accompanist = "0.32.0"
fastAdapter = "5.7.0"
preferencesFix = "1.1.0"
timber = "5.0.1"
-koinCore = "3.4.2"
-koinAndroid = "3.4.2"
-koinAndroidCompose = "3.4.5"
-koinTest = "3.4.1"
+koinCore = "3.5.0"
+koinAndroid = "3.5.0"
+koinAndroidCompose = "3.5.0"
+koinTest = "3.5.0"
mime4j = "0.8.9"
okhttp = "4.11.0"
-glide = "4.15.1"
+glide = "4.16.0"
moshi = "1.15.0"
-mockito = "5.3.1"
+mockito = "5.5.0"
datastore = "1.0.0"
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
android-lint = { id = "com.android.lint", version.ref = "androidGradlePlugin" }
-ksp = "com.google.devtools.ksp:1.8.21-1.0.11"
+ksp = "com.google.devtools.ksp:1.9.10-1.0.13"
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
-spotless = "com.diffplug.spotless:6.14.0"
+spotless = "com.diffplug.spotless:6.21.0"
detekt = "io.gitlab.arturbosch.detekt:1.23.0"
-dependency-check = "com.github.ben-manes.versions:0.46.0"
+dependency-check = "com.github.ben-manes.versions:0.48.0"
[libraries]
desugar = "com.android.tools:desugar_jdk_libs:2.0.3"
@@ -59,7 +61,7 @@ kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref =
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }
-kotlinx-datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0"
+kotlinx-datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.1"
kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5"
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppCompat" }
@@ -70,7 +72,7 @@ androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-lived
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidxLifecycle" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" }
-androidx-annotation = "androidx.annotation:annotation:1.6.0"
+androidx-annotation = "androidx.annotation:annotation:1.7.0"
androidx-biometric = "androidx.biometric:biometric:1.1.0"
androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment", version.ref = "androidxNavigation" }
androidx-navigation-ui = { module = "androidx.navigation:navigation-ui", version.ref = "androidxNavigation" }
@@ -94,6 +96,8 @@ androidx-compose-activity = { module = "androidx.activity:activity-compose", ver
androidx-compose-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" }
androidx-compose-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidxComposeMaterial" }
+androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "androidxComposeMaterial3" }
+androidx-compose-material3-windowSizeClass = { module = "androidx.compose.material3:material3-window-size-class", version.ref = "androidxComposeMaterial3" }
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidxComposeMaterial" }
androidx-compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" }
androidx-test-core = "androidx.test:core:1.5.0"
@@ -109,7 +113,7 @@ materialdrawer = "com.mikepenz:materialdrawer:8.4.5"
preferencex = { module = "com.takisoft.preferencex:preferencex", version.ref = "preferencesFix" }
preferencex-datetimepicker = { module = "com.takisoft.preferencex:preferencex-datetimepicker", version.ref = "preferencesFix" }
preferencex-colorpicker = { module = "com.takisoft.preferencex:preferencex-colorpicker", version.ref = "preferencesFix" }
-okio = "com.squareup.okio:okio:3.3.0"
+okio = "com.squareup.okio:okio:3.5.0"
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
@@ -151,9 +155,9 @@ yetanotheraccountchip = "com.github.fahim44:YetAnotherAccountChip:1.0.0"
webkit = "androidx.webkit:webkit:1.6.0"
junit = "junit:junit:4.13.2"
-robolectric = "org.robolectric:robolectric:4.9.2"
+robolectric = "org.robolectric:robolectric:4.10.3"
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
-mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:4.1.0"
+mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:5.1.0"
turbine = "app.cash.turbine:turbine:0.13.0"
jdom2 = "org.jdom:jdom2:2.0.6.1"
icu4j-charset = "com.ibm.icu:icu4j-charset:72.1"
@@ -161,12 +165,13 @@ assertk = "com.willowtreeapps.assertk:assertk-jvm:0.26.1"
leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.9.1"
-detekt-plugin-compose = "io.nlopez.compose.rules:detekt:0.1.7"
+detekt-plugin-compose = "io.nlopez.compose.rules:detekt:0.1.11"
[bundles]
shared-jvm-main = [
"koin-core",
"kotlinx-datetime",
+ "kotlinx-coroutines-core",
]
shared-jvm-android = [
"androidx-core",
@@ -196,6 +201,7 @@ shared-jvm-androidtest-compose = [
]
shared-jvm-test = [
"kotlin-test",
+ "kotlinx-coroutines-test",
"junit",
"assertk",
"mockito-core",
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index c1962a79e29d3e0ab67b14947c167a862655af9b..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 361e65ad54b59f140e325f15bd732b985501c690..e46a01fd0244a36b766169091425940aa8f87a88 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,8 @@
#Mon May 08 13:06:44 BDT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index aeb74cbb43e3931a2455a838345c3f6b8131aaa2..0adc8e1a53214b5f72ec3dfc95f7eacd239b7f27 100755
--- a/gradlew
+++ b/gradlew
@@ -83,7 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -130,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
diff --git a/images/K-9_Mail-no-shadows-debug.svg b/images/K-9_Mail-no-shadows-debug.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3468f3320544867531c079d1b4a0fdda42a860a7
--- /dev/null
+++ b/images/K-9_Mail-no-shadows-debug.svg
@@ -0,0 +1,180 @@
+
+
+
+
diff --git a/images/K-9_Mail-no-shadows.svg b/images/K-9_Mail-no-shadows.svg
new file mode 100644
index 0000000000000000000000000000000000000000..11d59a444133d2d28dfc306f1ab10584f4a53323
--- /dev/null
+++ b/images/K-9_Mail-no-shadows.svg
@@ -0,0 +1,180 @@
+
+
+
+
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeHeaderEncoder.kt b/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeHeaderEncoder.kt
index 6080afbbce2c72b381d4d6db48f4684bc100fe16..9654381e4040ef9c14465d0b832bc437860e046e 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeHeaderEncoder.kt
+++ b/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeHeaderEncoder.kt
@@ -17,10 +17,12 @@ object MimeHeaderEncoder {
}
private fun exceedsRecommendedLineLength(name: String, value: String): Boolean {
- return name.length + 2 /* colon + space */ + value.length > RECOMMENDED_MAX_LINE_LENGTH
+ return name.length + COLON_PLUS_SPACE_LENGTH + value.length > RECOMMENDED_MAX_LINE_LENGTH
}
private fun charactersNeedEncoding(text: String): Boolean {
return text.any { !it.isVChar() && !it.isWspOrCrlf() }
}
+
+ private const val COLON_PLUS_SPACE_LENGTH = 2
}
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeParameterEncoder.kt b/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeParameterEncoder.kt
index fe6b861a4a3edc131fb5613bb820ee6c52a1ce2f..82a4b40929afe1a5b03623f96a8bc843f15497d7 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeParameterEncoder.kt
+++ b/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeParameterEncoder.kt
@@ -13,6 +13,12 @@ object MimeParameterEncoder {
private const val ENCODED_VALUE_PREFIX = "UTF-8''"
+ private const val FOLDING_SPACE_LENGTH = 1
+ private const val EQUAL_SIGN_LENGTH = 1
+ private const val SEMICOLON_LENGTH = 1
+ private const val QUOTES_LENGTH = 2
+ private const val ASTERISK_LENGTH = 1
+
/**
* Create header field value with parameters encoded if necessary.
*/
@@ -35,9 +41,9 @@ object MimeParameterEncoder {
}
private fun StringBuilder.encodeAndAppendParameter(name: String, value: String) {
- val fixedCostLength = 1 /* folding space */ + name.length + 1 /* equals sign */ + 1 /* semicolon */
+ val fixedCostLength = FOLDING_SPACE_LENGTH + name.length + EQUAL_SIGN_LENGTH + SEMICOLON_LENGTH
val unencodedValueFitsOnSingleLine = fixedCostLength + value.length <= MAX_LINE_LENGTH
- val quotedValueMightFitOnSingleLine = fixedCostLength + value.length + 2 /* quotes */ <= MAX_LINE_LENGTH
+ val quotedValueMightFitOnSingleLine = fixedCostLength + value.length + QUOTES_LENGTH <= MAX_LINE_LENGTH
if (unencodedValueFitsOnSingleLine && value.isToken()) {
appendParameter(name, value)
@@ -56,8 +62,8 @@ object MimeParameterEncoder {
}
private fun StringBuilder.rfc2231EncodeAndAppendParameter(name: String, value: String) {
- val encodedValueLength = 1 /* folding space */ + name.length + 1 /* asterisk */ + 1 /* equal sign */ +
- ENCODED_VALUE_PREFIX.length + value.rfc2231EncodedLength() + 1 /* semicolon */
+ val encodedValueLength = FOLDING_SPACE_LENGTH + name.length + ASTERISK_LENGTH + EQUAL_SIGN_LENGTH +
+ ENCODED_VALUE_PREFIX.length + value.rfc2231EncodedLength() + SEMICOLON_LENGTH
if (encodedValueLength <= MAX_LINE_LENGTH) {
appendRfc2231SingleLineParameter(name, value.rfc2231Encoded())
@@ -88,7 +94,7 @@ object MimeParameterEncoder {
append(ENCODED_VALUE_PREFIX)
}
- remainingSpaceInLine = MAX_LINE_LENGTH - (length - lineStartIndex) - 1 /* semicolon */
+ remainingSpaceInLine = MAX_LINE_LENGTH - (length - lineStartIndex) - SEMICOLON_LENGTH
if (remainingSpaceInLine < 3) {
throw UnsupportedOperationException("Parameter name too long")
}
@@ -182,7 +188,7 @@ object MimeParameterEncoder {
}
private fun String.quotedLength(): Int {
- var length = 2 /* start and end quote */
+ var length = QUOTES_LENGTH
for (c in this) {
if (c.isQText() || c.isWsp()) {
length++
diff --git a/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeHeaderCheckerTest.kt b/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeHeaderCheckerTest.kt
index 9c5ed4543bef2298f49e66499ced1e02419b0be7..b53d12d2a6e8038b54202d70937f2222139934c3 100644
--- a/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeHeaderCheckerTest.kt
+++ b/mail/common/src/test/java/com/fsck/k9/mail/internet/MimeHeaderCheckerTest.kt
@@ -58,25 +58,27 @@ class MimeHeaderCheckerTest {
@Test
fun singleLineAtMaximumLineLength() {
- val longText = "x".repeat(998 /* text limit */ - 4 /* Test */ - 2 /* colon, space */)
+ val test = 4
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - test - COLON_PLUS_SPACE_LENGTH)
assertValidHeader("Test: $longText")
}
@Test
fun firstLineAtMaximumLineLength() {
- val longText = "x".repeat(998 /* text limit */ - 4 /* Test */ - 2 /* colon, space */)
+ val test = 4
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - test - COLON_PLUS_SPACE_LENGTH)
assertValidHeader("Test: $longText\r\n Text")
}
@Test
fun middleLineAtMaximumLineLength() {
- val longText = "x".repeat(998 - 1 /* space */)
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - SPACE_LENGTH)
assertValidHeader("Test: One\r\n $longText\r\n Three")
}
@Test
fun lastLineAtMaximumLineLength() {
- val longText = "x".repeat(998 - 1 /* space */)
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - SPACE_LENGTH)
assertValidHeader("Test: One\r\n $longText")
}
@@ -92,7 +94,7 @@ class MimeHeaderCheckerTest {
@Test
fun headerNameExceedingLineLimit() {
- val longName = "x".repeat(998 - 2 /* space, colon */ + 1)
+ val longName = "x".repeat(MAXIMUM_LINE_LENGTH - COLON_PLUS_SPACE_LENGTH + 1)
assertInvalidHeader("$longName: ")
}
@@ -173,25 +175,28 @@ class MimeHeaderCheckerTest {
@Test
fun singleLineExceedingLineLength() {
- val longText = "x".repeat(998 /* text limit */ - 4 /* Test */ - 2 /* colon, space */ + 1)
+ val test = 4
+
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - test - COLON_PLUS_SPACE_LENGTH + 1)
assertInvalidHeader("Test: $longText")
}
@Test
fun firstLineExceedingLineLength() {
- val longText = "x".repeat(998 /* text limit */ - 4 /* Test */ - 2 /* colon, space */ + 1)
+ val test = 4
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - test - COLON_PLUS_SPACE_LENGTH + 1)
assertInvalidHeader("Test: $longText\r\n Text")
}
@Test
fun middleLineExceedingLineLength() {
- val longText = "x".repeat(998 - 1 /* space */ + 1)
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - SPACE_LENGTH + 1)
assertInvalidHeader("Test: One\r\n $longText\r\n Three")
}
@Test
fun lastLineExceedingLineLength() {
- val longText = "x".repeat(998 - 1 /* space */ + 1)
+ val longText = "x".repeat(MAXIMUM_LINE_LENGTH - SPACE_LENGTH + 1)
assertInvalidHeader("Test: One\r\n $longText")
}
@@ -208,4 +213,10 @@ class MimeHeaderCheckerTest {
MimeHeaderChecker.checkHeader(name, value)
}.isInstanceOf()
}
+
+ private companion object {
+ private const val MAXIMUM_LINE_LENGTH = 998
+ private const val COLON_PLUS_SPACE_LENGTH = 2
+ private const val SPACE_LENGTH = 1
+ }
}
diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
index 26ff4103d486c0be1dbb4fea30c3159d96abd208..a80d0ec3fec95d44bc2d0fca9a60a5c684ec0d4a 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
@@ -100,7 +100,8 @@ internal class RealImapFolder(
try {
return executeSimpleCommand(Commands.NOOP)
} catch (ioe: IOException) {
- /* don't throw */ ioExceptionHandler(connection, ioe)
+ /* don't throw */
+ ioExceptionHandler(connection, ioe)
}
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 25d249ad6b1dcb400fb97d480e4a885329f3aa38..e12eb2c6acc049e42e2460d70de976ada05d84b3 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -41,10 +41,20 @@ include(
include(
":feature:launcher",
+ ":feature:onboarding",
+)
+
+include(
":feature:account:common",
- ":feature:account:setup",
+ ":feature:account:edit",
":feature:account:oauth",
- ":feature:onboarding",
+ ":feature:account:setup",
+ ":feature:account:server:certificate",
+ ":feature:account:server:settings",
+ ":feature:account:server:validation",
+)
+
+include(
":feature:autodiscovery:api",
":feature:autodiscovery:providersxml",
":feature:autodiscovery:srvrecords",