From 69def8fb5ab59b1804701c0104a320997d3683a0 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 3 Oct 2023 19:50:53 +0200 Subject: [PATCH 01/11] Rename .java to .kt (cherry picked from commit 6650f8f620e162f7d0ea037b3bbbee0c3f19f923) --- .../{SettingsImporterTest.java => SettingsImporterTest.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/core/src/test/java/com/fsck/k9/preferences/{SettingsImporterTest.java => SettingsImporterTest.kt} (100%) diff --git a/app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.java b/app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.kt similarity index 100% rename from app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.java rename to app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.kt -- GitLab From 57aef26cc61522fe14b7f241e3c2ef6f7ffd16f7 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 3 Oct 2023 23:50:54 +0600 Subject: [PATCH 02/11] Convert `SettingsImporterTest` to Kotlin (cherry picked from commit 7989c67c3471937feb7c34e5580331af4e5ec8d2) --- .../k9/preferences/SettingsImporterTest.kt | 457 +++++++++++------- 1 file changed, 273 insertions(+), 184 deletions(-) diff --git a/app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.kt b/app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.kt index 5ec36956d8..fe1af0e330 100644 --- a/app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.kt +++ b/app/core/src/test/java/com/fsck/k9/preferences/SettingsImporterTest.kt @@ -1,229 +1,318 @@ -package com.fsck.k9.preferences; - - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import android.content.Context; - -import com.fsck.k9.K9RobolectricTest; -import com.fsck.k9.Preferences; -import com.fsck.k9.mail.AuthType; -import kotlin.text.Charsets; -import okio.Buffer; -import org.junit.Before; -import org.junit.Test; -import org.robolectric.RuntimeEnvironment; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - - -public class SettingsImporterTest extends K9RobolectricTest { - private final Context context = RuntimeEnvironment.getApplication(); +package com.fsck.k9.preferences + +import android.content.Context +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.containsExactly +import assertk.assertions.extracting +import assertk.assertions.first +import assertk.assertions.hasSize +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isInstanceOf +import assertk.assertions.isTrue +import assertk.assertions.key +import assertk.assertions.prop +import com.fsck.k9.K9RobolectricTest +import com.fsck.k9.Preferences +import com.fsck.k9.mail.AuthType +import com.fsck.k9.preferences.SettingsImporter.AccountDescription +import com.fsck.k9.preferences.SettingsImporter.AccountDescriptionPair +import com.fsck.k9.preferences.SettingsImporter.ImportContents +import com.fsck.k9.preferences.SettingsImporter.ImportResults +import com.fsck.k9.preferences.SettingsImporter.ImportedAccount +import com.fsck.k9.preferences.SettingsImporter.ImportedIdentity +import com.fsck.k9.preferences.SettingsImporter.ImportedServer +import java.util.UUID +import org.junit.Before +import org.junit.Test +import org.robolectric.RuntimeEnvironment + +class SettingsImporterTest : K9RobolectricTest() { + private val context: Context = RuntimeEnvironment.getApplication() @Before - public void before() { - deletePreExistingAccounts(); + fun before() { + deletePreExistingAccounts() } - private void deletePreExistingAccounts() { - Preferences preferences = Preferences.getPreferences(); - preferences.clearAccounts(); + private fun deletePreExistingAccounts() { + val preferences = Preferences.getPreferences() + preferences.clearAccounts() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnBlankFile() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throw on empty file`() { + val inputStream = "".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnMissingFormat() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throw on missing format attribute`() { + val inputStream = """""".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnInvalidFormat() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throw on invalid format attribute value`() { + val inputStream = """""".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnNonPositiveFormat() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throw on invalid format version`() { + val inputStream = """""".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnMissingVersion() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throw on missing version attribute`() { + val inputStream = """""".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnInvalidVersion() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throws on invalid version attribute value`() { + val inputStream = """""".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } - @Test(expected = SettingsImportExportException.class) - public void importSettings_throwsExceptionOnNonPositiveVersion() throws SettingsImportExportException { - InputStream inputStream = inputStreamOf(""); - List accountUuids = new ArrayList<>(); + @Test + fun `importSettings() should throw on invalid version`() { + val inputStream = """""".byteInputStream() + val accountUuids = emptyList() - SettingsImporter.importSettings(context, inputStream, true, accountUuids, true); + assertFailure { + SettingsImporter.importSettings(context, inputStream, true, accountUuids, true) + }.isInstanceOf() } @Test - public void parseSettings_account() throws SettingsImportExportException { - String validUUID = UUID.randomUUID().toString(); - InputStream inputStream = inputStreamOf("" + - "Account"); - List accountUuids = new ArrayList<>(); - accountUuids.add("1"); - - SettingsImporter.Imported results = SettingsImporter.parseSettings(inputStream, true, accountUuids, true); - - assertEquals(1, results.accounts.size()); - assertEquals("Account", results.accounts.get(validUUID).name); - assertEquals(validUUID, results.accounts.get(validUUID).uuid); + fun `parseSettings() should return accounts`() { + val accountUuid = UUID.randomUUID().toString() + val inputStream = + """ + + + + Account + + + + """.trimIndent().byteInputStream() + val accountUuids = listOf("1") + + val results = SettingsImporter.parseSettings(inputStream, true, accountUuids, true) + + assertThat(results.accounts).all { + hasSize(1) + key(accountUuid).all { + prop(ImportedAccount::uuid).isEqualTo(accountUuid) + prop(ImportedAccount::name).isEqualTo("Account") + } + } } @Test - public void parseSettings_account_identities() throws SettingsImportExportException { - String validUUID = UUID.randomUUID().toString(); - InputStream inputStream = inputStreamOf("" + - "Account" + - "user@gmail.com" + - ""); - List accountUuids = new ArrayList<>(); - accountUuids.add("1"); - - SettingsImporter.Imported results = SettingsImporter.parseSettings(inputStream, true, accountUuids, true); - - assertEquals(1, results.accounts.size()); - assertEquals(validUUID, results.accounts.get(validUUID).uuid); - assertEquals(1, results.accounts.get(validUUID).identities.size()); - assertEquals("user@gmail.com", results.accounts.get(validUUID).identities.get(0).email); + fun `parseSettings() should return identities`() { + val accountUuid = UUID.randomUUID().toString() + val inputStream = + """ + + + + Account + + + user@gmail.com + + + + + + """.trimIndent().byteInputStream() + val accountUuids = listOf("1") + + val results = SettingsImporter.parseSettings(inputStream, true, accountUuids, true) + + assertThat(results.accounts).all { + hasSize(1) + key(accountUuid).all { + prop(ImportedAccount::uuid).isEqualTo(accountUuid) + prop(ImportedAccount::identities).extracting(ImportedIdentity::email).containsExactly("user@gmail.com") + } + } } - @Test - public void parseSettings_account_cram_md5() throws SettingsImportExportException { - String validUUID = UUID.randomUUID().toString(); - InputStream inputStream = inputStreamOf("" + - "Account" + - "CRAM_MD5" + - ""); - List accountUuids = new ArrayList<>(); - accountUuids.add(validUUID); - - SettingsImporter.Imported results = SettingsImporter.parseSettings(inputStream, true, accountUuids, false); - - assertEquals("Account", results.accounts.get(validUUID).name); - assertEquals(validUUID, results.accounts.get(validUUID).uuid); - assertEquals(AuthType.CRAM_MD5, results.accounts.get(validUUID).incoming.authenticationType); + fun `parseSettings() should parse incoming server authentication type`() { + val accountUuid = UUID.randomUUID().toString() + val inputStream = + """ + + + + Account + + CRAM_MD5 + + + + + """.trimIndent().byteInputStream() + val accountUuids = listOf(accountUuid) + + val results = SettingsImporter.parseSettings(inputStream, true, accountUuids, false) + + assertThat(results.accounts) + .key(accountUuid) + .prop(ImportedAccount::incoming) + .prop(ImportedServer::authenticationType) + .isEqualTo(AuthType.CRAM_MD5) } @Test - public void importSettings_disablesAccountsNeedingPasswords() throws SettingsImportExportException { - String validUUID = UUID.randomUUID().toString(); - InputStream inputStream = inputStreamOf("" + - "Account" + - "" + - "SSL_TLS_REQUIRED" + - "user@gmail.com" + - "CRAM_MD5" + - "googlemail.com" + - "" + - "" + - "SSL_TLS_REQUIRED" + - "user@googlemail.com" + - "CRAM_MD5" + - "googlemail.com" + - "" + - "b" + - "user@gmail.com" + - ""); - List accountUuids = new ArrayList<>(); - accountUuids.add(validUUID); - - SettingsImporter.ImportResults results = SettingsImporter.importSettings( - context, inputStream, true, accountUuids, false); - - assertEquals(0, results.erroneousAccounts.size()); - assertEquals(1, results.importedAccounts.size()); - assertEquals("Account", results.importedAccounts.get(0).imported.name); - assertEquals(validUUID, results.importedAccounts.get(0).imported.uuid); - assertTrue(results.importedAccounts.get(0).incomingPasswordNeeded); - assertTrue(results.importedAccounts.get(0).outgoingPasswordNeeded); + fun `importSettings() should disable accounts needing passwords`() { + val accountUuid = UUID.randomUUID().toString() + val inputStream = + """ + + + + Account + + SSL_TLS_REQUIRED + user@gmail.com + CRAM_MD5 + googlemail.com + + + SSL_TLS_REQUIRED + user@googlemail.com + CRAM_MD5 + googlemail.com + + + b + + + + user@gmail.com + + + + + + """.trimIndent().byteInputStream() + val accountUuids = listOf(accountUuid) + + val results = SettingsImporter.importSettings(context, inputStream, true, accountUuids, false) + + assertThat(results).all { + prop(ImportResults::erroneousAccounts).isEmpty() + prop(ImportResults::importedAccounts).all { + hasSize(1) + first().all { + prop(AccountDescriptionPair::imported).all { + prop(AccountDescription::uuid).isEqualTo(accountUuid) + prop(AccountDescription::name).isEqualTo("Account") + } + prop(AccountDescriptionPair::incomingPasswordNeeded).isTrue() + prop(AccountDescriptionPair::outgoingPasswordNeeded).isTrue() + } + } + } } @Test - public void getImportStreamContents_account() throws SettingsImportExportException { - String validUUID = UUID.randomUUID().toString(); - InputStream inputStream = inputStreamOf("" + - "" + - "" + - "Account" + - "" + - "" + - "user@gmail.com" + - "" + - "" + - "" + - ""); - - SettingsImporter.ImportContents results = SettingsImporter.getImportStreamContents(inputStream); - - assertEquals(false, results.globalSettings); - assertEquals(1, results.accounts.size()); - assertEquals("Account", results.accounts.get(0).name); - assertEquals(validUUID, results.accounts.get(0).uuid); + fun `getImportStreamContents() should return list of accounts`() { + val accountUuid = UUID.randomUUID().toString() + val inputStream = + """ + + + + Account + + + user@gmail.com + + + + + + """.trimIndent().byteInputStream() + + val results = SettingsImporter.getImportStreamContents(inputStream) + + assertThat(results).all { + prop(ImportContents::globalSettings).isFalse() + prop(ImportContents::accounts).all { + hasSize(1) + first().all { + prop(AccountDescription::uuid).isEqualTo(accountUuid) + prop(AccountDescription::name).isEqualTo("Account") + } + } + } } @Test - public void getImportStreamContents_alternativeName() throws SettingsImportExportException { - String validUUID = UUID.randomUUID().toString(); - InputStream inputStream = inputStreamOf("" + - "" + - "" + - "" + - "" + - "" + - "user@gmail.com" + - "" + - "" + - "" + - ""); - - SettingsImporter.ImportContents results = SettingsImporter.getImportStreamContents(inputStream); - - assertEquals(false, results.globalSettings); - assertEquals(1, results.accounts.size()); - assertEquals("user@gmail.com", results.accounts.get(0).name); - assertEquals(validUUID, results.accounts.get(0).uuid); - } - - private InputStream inputStreamOf(String data) { - return new Buffer() - .writeString(data, Charsets.UTF_8) - .inputStream(); + fun `getImportStreamContents() should return email address as account name when no account name provided`() { + val accountUuid = UUID.randomUUID().toString() + val inputStream = + """ + + + + + + + user@gmail.com + + + + + + """.trimIndent().byteInputStream() + + val results = SettingsImporter.getImportStreamContents(inputStream) + + assertThat(results).all { + prop(ImportContents::globalSettings).isFalse() + prop(ImportContents::accounts).all { + hasSize(1) + first().all { + prop(AccountDescription::uuid).isEqualTo(accountUuid) + prop(AccountDescription::name).isEqualTo("user@gmail.com") + } + } + } } } -- GitLab From 05d15b4c9f455b60aa0d07aa57132fd8688ecc92 Mon Sep 17 00:00:00 2001 From: cketti Date: Wed, 4 Oct 2023 12:58:29 +0200 Subject: [PATCH 03/11] Prepare for version 6.712 (cherry picked from commit 8248b220c36ae730c6c9338238b79f6d41fecde0) --- app/k9mail/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/k9mail/build.gradle.kts b/app/k9mail/build.gradle.kts index 88d056375e..de2a72c97d 100644 --- a/app/k9mail/build.gradle.kts +++ b/app/k9mail/build.gradle.kts @@ -66,7 +66,7 @@ android { testApplicationId = "foundation.e.mail.tests" versionCode = 37011 - versionName = "6.711" + versionName = "6.712-SNAPSHOT" // Keep in sync with the resource string array "supported_languages" resourceConfigurations.addAll( -- GitLab From 3a0802d7841972e81980be8bd7604182b9feadd1 Mon Sep 17 00:00:00 2001 From: cketti Date: Wed, 4 Oct 2023 14:42:26 +0200 Subject: [PATCH 04/11] Add instructions on how to create a beta release on F-Droid (cherry picked from commit 5d5ff699b10d90f633fbe81b65796c6fabbaf216) --- docs/RELEASING.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 52f1e7ad31..4ef950b36b 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -11,6 +11,12 @@ k9mail.keyPassword= ``` +## One-time setup for F-Droid builds + +1. Install *fdroidserver* by following the [installation instructions](https://f-droid.org/docs/Installing_the_Server_and_Repo_Tools). +2. [Sign up for a Gitlab account](https://gitlab.com/users/sign_up) and fork the [fdroiddata](https://gitlab.com/fdroid/fdroiddata) repository. +3. Clone your fork of the *fdroiddata* repository. + ## Release a beta version 1. Update versionCode and versionName in `app/k9mail/build.gradle` @@ -49,7 +55,15 @@ ### Create release on F-Droid -TODO +1. Fetch the latest changes from the *fdroiddata* repository. +2. Switch to a new branch in your copy of the *fdroiddata* repository. +3. Edit `metadata/com.fsck.k9.yml` to create a new entry for the version you want to release. Usually it's copy & paste of the previous entry and adjusting `versionName`, `versionCode`, and `commit` (use the tag name). Leave `CurrentVersion` and `CurrentVersionCode` unchanged. Those specify which version is the stable/recommended build. +4. Commit the changes. Message: "Update K-9 Mail to $newVersionName" +5. Run `fdroid build --latest com.fsck.k9` to build the project using F-Droid's toolchain. +6. Push the changes to your fork of the *fdroiddata* repository. +7. Open a merge request on Gitlab. (The message from the server after the push in the previous step should contain a URL) +8. Select the *App update* template and fill it out. +9. Create merge request and the F-Droid team will do the rest. ### Create release on Google Play -- GitLab From 41c994cf30f631767c4ea2af98f6982ce8e2942f Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 5 Oct 2023 15:50:04 +0200 Subject: [PATCH 05/11] Reformat Markdown file using `spotlessApply` (cherry picked from commit 6c0a21ee60e5c1831f79ebb2f1a0e7de2463478c) --- docs/RELEASING.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 4ef950b36b..faa84be720 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -13,9 +13,9 @@ ## One-time setup for F-Droid builds -1. Install *fdroidserver* by following the [installation instructions](https://f-droid.org/docs/Installing_the_Server_and_Repo_Tools). -2. [Sign up for a Gitlab account](https://gitlab.com/users/sign_up) and fork the [fdroiddata](https://gitlab.com/fdroid/fdroiddata) repository. -3. Clone your fork of the *fdroiddata* repository. +1. Install _fdroidserver_ by following the [installation instructions](https://f-droid.org/docs/Installing_the_Server_and_Repo_Tools). +2. [Sign up for a Gitlab account](https://gitlab.com/users/sign_up) and fork the [fdroiddata](https://gitlab.com/fdroid/fdroiddata) repository. +3. Clone your fork of the _fdroiddata_ repository. ## Release a beta version @@ -55,14 +55,14 @@ ### Create release on F-Droid -1. Fetch the latest changes from the *fdroiddata* repository. -2. Switch to a new branch in your copy of the *fdroiddata* repository. +1. Fetch the latest changes from the _fdroiddata_ repository. +2. Switch to a new branch in your copy of the _fdroiddata_ repository. 3. Edit `metadata/com.fsck.k9.yml` to create a new entry for the version you want to release. Usually it's copy & paste of the previous entry and adjusting `versionName`, `versionCode`, and `commit` (use the tag name). Leave `CurrentVersion` and `CurrentVersionCode` unchanged. Those specify which version is the stable/recommended build. 4. Commit the changes. Message: "Update K-9 Mail to $newVersionName" -5. Run `fdroid build --latest com.fsck.k9` to build the project using F-Droid's toolchain. -6. Push the changes to your fork of the *fdroiddata* repository. +5. Run `fdroid build --latest com.fsck.k9` to build the project using F-Droid's toolchain. +6. Push the changes to your fork of the _fdroiddata_ repository. 7. Open a merge request on Gitlab. (The message from the server after the push in the previous step should contain a URL) -8. Select the *App update* template and fill it out. +8. Select the _App update_ template and fill it out. 9. Create merge request and the F-Droid team will do the rest. ### Create release on Google Play -- GitLab From e5761e7a930bee082f059992ac79fdab69b882cd Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 5 Oct 2023 15:38:50 +0200 Subject: [PATCH 06/11] Fix crash when validating email address input (cherry picked from commit 7facf4e44e9855956e6b6f49bd5b6364db5f9b24) --- .../account/setup/ui/autodiscovery/item/EmailAddressItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b4fd9d522f..5e4d61ea91 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.server.settings.ui.common.mapper.toResourceString +import app.k9mail.feature.account.setup.ui.autodiscovery.toResourceString @Composable internal fun LazyItemScope.EmailAddressItem( -- GitLab From 598efc0ac6ccdbfd757331ebca99642c7a4adfc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Thu, 5 Oct 2023 21:08:50 +0600 Subject: [PATCH 07/11] Add ADR readme and template (cherry picked from commit 950b9d7ba5b30dd13eeb2d58572048253ca5acec) --- docs/architecture/adr/0000-adr-template.md | 29 ++++++++++++ docs/architecture/adr/README.md | 51 ++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 docs/architecture/adr/0000-adr-template.md create mode 100644 docs/architecture/adr/README.md diff --git a/docs/architecture/adr/0000-adr-template.md b/docs/architecture/adr/0000-adr-template.md new file mode 100644 index 0000000000..5d752b15f2 --- /dev/null +++ b/docs/architecture/adr/0000-adr-template.md @@ -0,0 +1,29 @@ +# [NNNN] - [Descriptive Title in Title Case] + +## Status + + + +- **Status** + +## Context + + + +## Decision + + + +## Consequences + + + +- **Positive Consequences** + + - consequence 1 + - consequence 2 + +- **Negative Consequences** + + - consequence 1 + - consequence 2 diff --git a/docs/architecture/adr/README.md b/docs/architecture/adr/README.md new file mode 100644 index 0000000000..e57ff4fa87 --- /dev/null +++ b/docs/architecture/adr/README.md @@ -0,0 +1,51 @@ +# Architecture Decision Records + +This [folder](/docs/architecture/adr) contains the architecture decision records (ADRs) for our project. + +ADRs are short text documents that serve as a historical context for the architecture decisions we make over the +course of the project. + +## What is an ADR? + +An Architecture Decision Record (ADR) is a document that captures an important architectural decision made along +with its context and consequences. ADRs record the decision making process and allow others to understand the +rationale behind decisions, providing insight and facilitating future decision-making processes. + +## Format of an ADR + +We adhere to Michael Nygard's [ADR format proposal](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions), +where each ADR document should contain: + +1. **Title**: A short descriptive name for the decision. +2. **Status**: The current status of the decision (proposed, accepted, rejected, deprecated, superseded) +3. **Context**: The context that motivates this decision. +4. **Decision**: The change that we're proposing and/or doing. +5. **Consequences**: What becomes easier or more difficult to do and any risks introduced as a result of the decision. + +## Creating a new ADR + +When creating a new ADR, please follow the provided [ADR template file](0000-adr-template.md) and ensure that your +document is clear and concise. + +## Directory Structure + +The ADRs will be stored in a directory named `docs/adr`, and each ADR will be a file named `NNNN-title-with-dashes.md` +where `NNNN` is a four-digit number that is increased by 1 for every new adr. + +## ADR Life Cycle + +The life cycle of an ADR is as follows: + +1. **Proposed**: The ADR is under consideration. +2. **Accepted**: The decision described in the ADR has been accepted and should be adhered to, unless it is superseded by another ADR. +3. **Rejected**: The decision described in the ADR has been rejected. +4. **Deprecated**: The decision described in the ADR is no longer relevant due to changes in system context. +5. **Superseded**: The decision described in the ADR has been replaced by another decision. + +Each ADR will have a status indicating its current life-cycle stage. An ADR can be updated over time, either to change +the status or to add more information. + +## Contributions + +We welcome contributions in the form of new ADRs or updates to existing ones. Please ensure all contributions follow +the standard format and provide clear and concise information. -- GitLab From 16bc648344c953b52b74eb0ea0a373a008dbe4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Thu, 5 Oct 2023 17:10:52 +0200 Subject: [PATCH 08/11] Add ADR 0001 about Java to Kotlin switch (cherry picked from commit bec885475de5667bb517176986a851758b6af910) --- .../adr/0001-switch-from-java-to-kotlin.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/architecture/adr/0001-switch-from-java-to-kotlin.md diff --git a/docs/architecture/adr/0001-switch-from-java-to-kotlin.md b/docs/architecture/adr/0001-switch-from-java-to-kotlin.md new file mode 100644 index 0000000000..8ae1e5aa04 --- /dev/null +++ b/docs/architecture/adr/0001-switch-from-java-to-kotlin.md @@ -0,0 +1,32 @@ +# 0001 - Switch from Java to Kotlin + +## Status + +- **Accepted** + +## Context + +We've been using Java as our primary language for Android development. While Java has served us well, it has certain +limitations in terms of null safety, verbosity, functional programming, and more. Kotlin, officially supported by +Google for Android development, offers solutions to many of these issues and provides more modern language features +that can improve productivity, maintainability, and overall code quality. + +## Decision + +Switch our primary programming language for Android development from Java to Kotlin. This will involve rewriting our +existing Java codebase in Kotlin and writing all new code in Kotlin. To facilitate the transition, we will gradually +refactor our existing Java codebase to Kotlin. + +## Consequences + +- **Positive Consequences** + + - Improved null safety, reducing potential for null pointer exceptions. + - Increased code readability and maintainability due to less verbose syntax. + - Availability of modern language features such as coroutines for asynchronous programming, and extension functions. + - Officially supported by Google for Android development, ensuring future-proof development. + +- **Negative Consequences** + + - The process of refactoring existing Java code to Kotlin can be time-consuming. + - Potential for introduction of new bugs during refactoring. -- GitLab From 5e91deb842633f7e1414934b70924e936ed0522f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 13 Sep 2023 17:53:41 +0200 Subject: [PATCH 09/11] Change AccountUpdater to AccountServerSettingsUpdater (cherry picked from commit 42a93bcfefc9644bc1103850eea7f13c588fb121) --- .../k9mail/feature/preview/FeatureModule.kt | 2 +- .../preview/account/InMemoryAccountStore.kt | 26 +++- .../java/com/fsck/k9/account/AccountModule.kt | 6 +- .../account/AccountServerSettingsUpdater.kt | 54 ++++++++ .../com/fsck/k9/account/AccountUpdater.kt | 44 ------ .../fsck/k9/account/AccountStateLoaderTest.kt | 14 +- .../com/fsck/k9/account/AccountUpdaterTest.kt | 131 ++++++++++++++++++ .../com/fsck/k9/account/FakeAccountManager.kt | 9 +- .../edit/AccountEditExternalContract.kt | 23 ++- 9 files changed, 238 insertions(+), 71 deletions(-) create mode 100644 app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt delete mode 100644 app/k9mail/src/main/java/com/fsck/k9/account/AccountUpdater.kt create mode 100644 app/k9mail/src/test/java/com/fsck/k9/account/AccountUpdaterTest.kt 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 a2b003825c..aa80e7390e 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 @@ -27,7 +27,7 @@ val accountModule: Module = module { arrayOf( AccountCommonExternalContract.AccountStateLoader::class, AccountSetupExternalContract.AccountCreator::class, - AccountEditExternalContract.AccountUpdater::class, + AccountEditExternalContract.AccountServerSettingsUpdater::class, ), ) factory { AccountOwnerNameProvider() } diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt index fdb4136509..f67e89c88f 100644 --- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt +++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt @@ -4,14 +4,16 @@ import app.k9mail.feature.account.common.AccountCommonExternalContract.AccountSt import app.k9mail.feature.account.common.domain.entity.Account import app.k9mail.feature.account.common.domain.entity.AccountState import app.k9mail.feature.account.common.domain.entity.AuthorizationState -import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdater -import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdater.AccountUpdaterResult +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountServerSettingsUpdater +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterFailure +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterResult import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult +import com.fsck.k9.mail.ServerSettings class InMemoryAccountStore( private val accountMap: MutableMap = mutableMapOf(), -) : AccountCreator, AccountUpdater, AccountStateLoader { +) : AccountCreator, AccountServerSettingsUpdater, AccountStateLoader { suspend fun load(accountUuid: String): Account? { return accountMap[accountUuid] @@ -27,11 +29,21 @@ class InMemoryAccountStore( return AccountCreatorResult.Success(account.uuid) } - override suspend fun updateAccount(account: Account): AccountUpdaterResult { - return if (!accountMap.containsKey(account.uuid)) { - AccountUpdaterResult.Error("Account not found") + override suspend fun updateServerSettings( + accountUuid: String, + isIncoming: Boolean, + serverSettings: ServerSettings, + ): AccountUpdaterResult { + return if (!accountMap.containsKey(accountUuid)) { + AccountUpdaterResult.Failure(AccountUpdaterFailure.AccountNotFound(accountUuid)) } else { - accountMap[account.uuid] = account + val account = accountMap[accountUuid]!! + + accountMap[account.uuid] = if (isIncoming) { + account.copy(incomingServerSettings = serverSettings) + } else { + account.copy(outgoingServerSettings = serverSettings) + } AccountUpdaterResult.Success(account.uuid) } diff --git a/app/k9mail/src/main/java/com/fsck/k9/account/AccountModule.kt b/app/k9mail/src/main/java/com/fsck/k9/account/AccountModule.kt index 677c544e7e..793405215f 100644 --- a/app/k9mail/src/main/java/com/fsck/k9/account/AccountModule.kt +++ b/app/k9mail/src/main/java/com/fsck/k9/account/AccountModule.kt @@ -28,9 +28,9 @@ val newAccountModule = module { ) } - factory { - AccountUpdater( - preferences = get(), + factory { + AccountServerSettingsUpdater( + accountManager = get(), ) } } diff --git a/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt b/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt new file mode 100644 index 0000000000..f91f30354c --- /dev/null +++ b/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt @@ -0,0 +1,54 @@ +package com.fsck.k9.account + +import app.k9mail.feature.account.edit.AccountEditExternalContract +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterFailure +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterResult +import com.fsck.k9.logging.Timber +import com.fsck.k9.mail.ServerSettings +import com.fsck.k9.preferences.AccountManager +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class AccountServerSettingsUpdater( + private val accountManager: AccountManager, + private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, +) : AccountEditExternalContract.AccountServerSettingsUpdater { + + @Suppress("TooGenericExceptionCaught") + override suspend fun updateServerSettings( + accountUuid: String, + isIncoming: Boolean, + serverSettings: ServerSettings, + ): AccountUpdaterResult { + return try { + withContext(coroutineDispatcher) { + updateSettings(accountUuid, isIncoming, serverSettings) + } + } catch (error: Exception) { + Timber.e(error, "Error while updating account server settings with UUID %s", accountUuid) + + AccountUpdaterResult.Failure(AccountUpdaterFailure.UnknownError(error)) + } + } + + private fun updateSettings( + accountUuid: String, + isIncoming: Boolean, + serverSettings: ServerSettings, + ): AccountUpdaterResult { + val account = accountManager.getAccount(accountUuid = accountUuid) ?: return AccountUpdaterResult.Failure( + AccountUpdaterFailure.AccountNotFound(accountUuid), + ) + + if (isIncoming) { + account.incomingServerSettings = serverSettings + } else { + account.outgoingServerSettings = serverSettings + } + + accountManager.saveAccount(account) + + return AccountUpdaterResult.Success(accountUuid) + } +} 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 deleted file mode 100644 index 3a5bd41431..0000000000 --- a/app/k9mail/src/main/java/com/fsck/k9/account/AccountUpdater.kt +++ /dev/null @@ -1,44 +0,0 @@ -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/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt b/app/k9mail/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt index be7b5f9402..234b62a701 100644 --- a/app/k9mail/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt +++ b/app/k9mail/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt @@ -16,18 +16,18 @@ import org.junit.Test class AccountStateLoaderTest { @Test - fun `loadAccountState() should return null when accountManager returns null`() = runTest { + fun `loadAccountState() SHOULD return null when accountManager returns null`() = runTest { val accountManager = FakeAccountManager() - val accountLoader = AccountStateLoader(accountManager) + val testSubject = AccountStateLoader(accountManager) - val result = accountLoader.loadAccountState("accountUuid") + val result = testSubject.loadAccountState("accountUuid") assertThat(result).isNull() } @Test - fun `loadAccountState() should return account when present in accountManager`() = runTest { - val accounts = mapOf( + fun `loadAccountState() SHOULD return account when present in accountManager`() = runTest { + val accounts = mutableMapOf( "accountUuid" to Account(uuid = "accountUuid").apply { identities = mutableListOf(Identity()) email = "emailAddress" @@ -37,9 +37,9 @@ class AccountStateLoaderTest { }, ) val accountManager = FakeAccountManager(accounts = accounts) - val accountLoader = AccountStateLoader(accountManager) + val testSubject = AccountStateLoader(accountManager) - val result = accountLoader.loadAccountState("accountUuid") + val result = testSubject.loadAccountState("accountUuid") assertThat(result).isEqualTo( AccountState( diff --git a/app/k9mail/src/test/java/com/fsck/k9/account/AccountUpdaterTest.kt b/app/k9mail/src/test/java/com/fsck/k9/account/AccountUpdaterTest.kt new file mode 100644 index 0000000000..8d9ef1ac2f --- /dev/null +++ b/app/k9mail/src/test/java/com/fsck/k9/account/AccountUpdaterTest.kt @@ -0,0 +1,131 @@ +package com.fsck.k9.account + +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterFailure +import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterResult +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import assertk.assertions.isNotNull +import assertk.assertions.prop +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 +import com.fsck.k9.Account as K9Account + +class AccountUpdaterTest { + + @Test + fun `updateServerSettings() SHOULD return account not found exception WHEN none present with uuid`() = runTest { + val accountManager = FakeAccountManager(accounts = mutableMapOf()) + val testSubject = AccountServerSettingsUpdater(accountManager) + + val result = testSubject.updateServerSettings( + accountUuid = ACCOUNT_UUID, + isIncoming = true, + serverSettings = INCOMING_SERVER_SETTINGS, + ) + + assertThat(result).isEqualTo(AccountUpdaterResult.Failure(AccountUpdaterFailure.AccountNotFound(ACCOUNT_UUID))) + } + + @Test + fun `updateServerSettings() SHOULD return success with updated incoming settings WHEN is incoming`() = runTest { + val accountManager = FakeAccountManager(accounts = mutableMapOf(ACCOUNT_UUID to createAccount(ACCOUNT_UUID))) + val updatedIncomingServerSettings = INCOMING_SERVER_SETTINGS.copy(port = 123) + val testSubject = AccountServerSettingsUpdater(accountManager) + + val result = testSubject.updateServerSettings( + accountUuid = ACCOUNT_UUID, + isIncoming = true, + serverSettings = updatedIncomingServerSettings, + ) + + assertThat(result).isEqualTo(AccountUpdaterResult.Success(ACCOUNT_UUID)) + + val k9Account = accountManager.getAccount(ACCOUNT_UUID) + assertThat(k9Account).isNotNull().all { + prop(K9Account::incomingServerSettings).isEqualTo(updatedIncomingServerSettings) + prop(K9Account::outgoingServerSettings).isEqualTo(OUTGOING_SERVER_SETTINGS) + } + } + + @Test + fun `updateServerSettings() SHOULD return success with updated outgoing settings WHEN is not incoming`() = runTest { + val accountManager = FakeAccountManager(accounts = mutableMapOf(ACCOUNT_UUID to createAccount(ACCOUNT_UUID))) + val updatedOutgoingServerSettings = OUTGOING_SERVER_SETTINGS.copy(port = 123) + val testSubject = AccountServerSettingsUpdater(accountManager) + + val result = testSubject.updateServerSettings( + accountUuid = ACCOUNT_UUID, + isIncoming = false, + serverSettings = updatedOutgoingServerSettings, + ) + + assertThat(result).isEqualTo(AccountUpdaterResult.Success(ACCOUNT_UUID)) + + val k9Account = accountManager.getAccount(ACCOUNT_UUID) + assertThat(k9Account).isNotNull().all { + prop(K9Account::incomingServerSettings).isEqualTo(INCOMING_SERVER_SETTINGS) + prop(K9Account::outgoingServerSettings).isEqualTo(updatedOutgoingServerSettings) + } + } + + @Test + fun `updateServerSettings() SHOULD return unknown error when exception thrown`() = runTest { + val accountManager = FakeAccountManager( + accounts = mutableMapOf(ACCOUNT_UUID to createAccount(ACCOUNT_UUID)), + isFailureOnSave = true, + ) + val testSubject = AccountServerSettingsUpdater(accountManager) + + val result = testSubject.updateServerSettings( + accountUuid = ACCOUNT_UUID, + isIncoming = true, + serverSettings = INCOMING_SERVER_SETTINGS, + ) + + assertThat(result).isInstanceOf() + .prop(AccountUpdaterResult.Failure::error).isInstanceOf() + .prop(AccountUpdaterFailure.UnknownError::error).isInstanceOf() + } + + private companion object { + const val ACCOUNT_UUID = "uuid" + + 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(), + ) + + fun createAccount(accountUuid: String): K9Account { + return K9Account( + uuid = accountUuid, + ).apply { + incomingServerSettings = INCOMING_SERVER_SETTINGS + outgoingServerSettings = OUTGOING_SERVER_SETTINGS + } + } + } +} 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 index d22868e2f0..57e78c6f14 100644 --- a/app/k9mail/src/test/java/com/fsck/k9/account/FakeAccountManager.kt +++ b/app/k9mail/src/test/java/com/fsck/k9/account/FakeAccountManager.kt @@ -7,7 +7,8 @@ import com.fsck.k9.preferences.AccountManager import kotlinx.coroutines.flow.Flow class FakeAccountManager( - private val accounts: Map = emptyMap(), + private val accounts: MutableMap = mutableMapOf(), + private val isFailureOnSave: Boolean = false, ) : AccountManager { override fun getAccountsFlow(): Flow> { @@ -36,7 +37,11 @@ class FakeAccountManager( TODO("Not yet implemented") } + @Suppress("TooGenericExceptionThrown") override fun saveAccount(account: Account) { - TODO("Not yet implemented") + if (isFailureOnSave) { + throw Exception("FakeAccountManager.saveAccount() failed") + } + accounts[account.uuid] = account } } 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 index d7c65cd0e1..6ce49b1439 100644 --- 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 @@ -1,15 +1,24 @@ package app.k9mail.feature.account.edit -import app.k9mail.feature.account.common.domain.entity.Account +import com.fsck.k9.mail.ServerSettings interface AccountEditExternalContract { - fun interface AccountUpdater { - suspend fun updateAccount(account: Account): AccountUpdaterResult + sealed interface AccountUpdaterResult { + data class Success(val message: String) : AccountUpdaterResult + data class Failure(val error: AccountUpdaterFailure) : AccountUpdaterResult + } + + sealed interface AccountUpdaterFailure { + data class AccountNotFound(val accountUuid: String) : AccountUpdaterFailure + data class UnknownError(val error: Exception) : AccountUpdaterFailure + } - sealed interface AccountUpdaterResult { - data class Success(val accountId: String) : AccountUpdaterResult - data class Error(val message: String) : AccountUpdaterResult - } + fun interface AccountServerSettingsUpdater { + suspend fun updateServerSettings( + accountUuid: String, + isIncoming: Boolean, + serverSettings: ServerSettings, + ): AccountUpdaterResult } } -- GitLab From 74ac97caf9dc4786ea31b8e96477b89ddb833379 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 5 Oct 2023 16:35:10 +0200 Subject: [PATCH 10/11] Extract `SettingsUpgrader` implementations to separate files (cherry picked from commit 67839f3f14888897efc658a4db7030baeca9566f) --- .../AccountSettingsDescriptions.java | 135 +++-------------- .../GeneralSettingsDescriptions.java | 142 ++---------------- .../com/fsck/k9/preferences/Settings.java | 2 +- .../upgrader/AccountSettingsUpgraderTo53.java | 34 +++++ .../upgrader/AccountSettingsUpgraderTo54.java | 26 ++++ .../upgrader/AccountSettingsUpgraderTo74.java | 23 +++ .../upgrader/AccountSettingsUpgraderTo80.java | 34 +++++ .../upgrader/AccountSettingsUpgraderTo81.java | 27 ++++ .../upgrader/GeneralSettingsUpgraderTo24.java | 29 ++++ .../upgrader/GeneralSettingsUpgraderTo31.java | 48 ++++++ .../upgrader/GeneralSettingsUpgraderTo58.java | 25 +++ .../upgrader/GeneralSettingsUpgraderTo69.java | 25 +++ .../upgrader/GeneralSettingsUpgraderTo79.java | 29 ++++ .../migrations/StorageMigrationTo11.kt | 4 +- 14 files changed, 330 insertions(+), 253 deletions(-) create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo53.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo54.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo74.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo80.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo81.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo24.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo31.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo58.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo69.java create mode 100644 app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo79.java diff --git a/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java b/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java index 58675c098a..8998067126 100644 --- a/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java +++ b/app/core/src/main/java/com/fsck/k9/preferences/AccountSettingsDescriptions.java @@ -26,7 +26,6 @@ import com.fsck.k9.K9; import com.fsck.k9.NotificationLight; import com.fsck.k9.core.R; import com.fsck.k9.mailstore.StorageManager; -import com.fsck.k9.notification.NotificationLightDecoder; import com.fsck.k9.preferences.Settings.BooleanSetting; import com.fsck.k9.preferences.Settings.ColorSetting; import com.fsck.k9.preferences.Settings.EnumSetting; @@ -37,7 +36,13 @@ import com.fsck.k9.preferences.Settings.SettingsDescription; import com.fsck.k9.preferences.Settings.SettingsUpgrader; import com.fsck.k9.preferences.Settings.StringSetting; import com.fsck.k9.preferences.Settings.V; -import kotlin.collections.SetsKt; +import com.fsck.k9.preferences.upgrader.AccountSettingsUpgraderTo53; +import com.fsck.k9.preferences.upgrader.AccountSettingsUpgraderTo54; +import com.fsck.k9.preferences.upgrader.AccountSettingsUpgraderTo74; +import com.fsck.k9.preferences.upgrader.AccountSettingsUpgraderTo80; +import com.fsck.k9.preferences.upgrader.AccountSettingsUpgraderTo81; + +import static com.fsck.k9.preferences.upgrader.AccountSettingsUpgraderTo53.FOLDER_NONE; public class AccountSettingsDescriptions { @@ -59,7 +64,7 @@ public class AccountSettingsDescriptions { new V(13, new BooleanSetting(false)) )); s.put("archiveFolderName", Settings.versions( - new V(1, new StringSetting(SettingsUpgraderV53.FOLDER_NONE)), + new V(1, new StringSetting(FOLDER_NONE)), new V(53, new StringSetting(null)) )); s.put("autoExpandFolderName", Settings.versions( @@ -84,7 +89,7 @@ public class AccountSettingsDescriptions { R.array.display_count_values)) )); s.put("draftsFolderName", Settings.versions( - new V(1, new StringSetting(SettingsUpgraderV53.FOLDER_NONE)), + new V(1, new StringSetting(FOLDER_NONE)), new V(53, new StringSetting(null)) )); s.put("expungePolicy", Settings.versions( @@ -174,7 +179,7 @@ public class AccountSettingsDescriptions { new V(1, new EnumSetting<>(Searchable.class, Searchable.ALL)) )); s.put("sentFolderName", Settings.versions( - new V(1, new StringSetting(SettingsUpgraderV53.FOLDER_NONE)), + new V(1, new StringSetting(FOLDER_NONE)), new V(53, new StringSetting(null)) )); s.put("sortTypeEnum", Settings.versions( @@ -190,7 +195,7 @@ public class AccountSettingsDescriptions { new V(1, new BooleanSetting(false)) )); s.put("spamFolderName", Settings.versions( - new V(1, new StringSetting(SettingsUpgraderV53.FOLDER_NONE)), + new V(1, new StringSetting(FOLDER_NONE)), new V(53, new StringSetting(null)) )); s.put("stripSignature", Settings.versions( @@ -203,7 +208,7 @@ public class AccountSettingsDescriptions { new V(1, new BooleanSetting(true)) )); s.put("trashFolderName", Settings.versions( - new V(1, new StringSetting(SettingsUpgraderV53.FOLDER_NONE)), + new V(1, new StringSetting(FOLDER_NONE)), new V(53, new StringSetting(null)) )); s.put("useCompression.MOBILE", Settings.versions( @@ -285,11 +290,11 @@ public class AccountSettingsDescriptions { SETTINGS = Collections.unmodifiableMap(s); Map u = new HashMap<>(); - u.put(53, new SettingsUpgraderV53()); - u.put(54, new SettingsUpgraderV54()); - u.put(74, new SettingsUpgraderV74()); - u.put(80, new SettingsUpgraderV80()); - u.put(81, new SettingsUpgraderV81()); + u.put(53, new AccountSettingsUpgraderTo53()); + u.put(54, new AccountSettingsUpgraderTo54()); + u.put(74, new AccountSettingsUpgraderTo74()); + u.put(80, new AccountSettingsUpgraderTo80()); + u.put(81, new AccountSettingsUpgraderTo81()); UPGRADERS = Collections.unmodifiableMap(u); } @@ -468,110 +473,4 @@ public class AccountSettingsDescriptions { } } - /** - * Upgrades settings from version 52 to 53 - * - * Replace folder entries of "-NONE-" with {@code null}. - */ - private static class SettingsUpgraderV53 implements SettingsUpgrader { - private static final String FOLDER_NONE = "-NONE-"; - - @Override - public Set upgrade(Map settings) { - upgradeFolderEntry(settings, "archiveFolderName"); - upgradeFolderEntry(settings, "autoExpandFolderName"); - upgradeFolderEntry(settings, "draftsFolderName"); - upgradeFolderEntry(settings, "sentFolderName"); - upgradeFolderEntry(settings, "spamFolderName"); - upgradeFolderEntry(settings, "trashFolderName"); - - return null; - } - - private void upgradeFolderEntry(Map settings, String key) { - String archiveFolderName = (String) settings.get(key); - if (FOLDER_NONE.equals(archiveFolderName)) { - settings.put(key, null); - } - } - } - - /** - * Upgrades settings from version 53 to 54 - * - * Inserts folder selection entries with a value of "MANUAL" - */ - private static class SettingsUpgraderV54 implements SettingsUpgrader { - private static final String FOLDER_SELECTION_MANUAL = "MANUAL"; - - @Override - public Set upgrade(Map settings) { - settings.put("archiveFolderSelection", FOLDER_SELECTION_MANUAL); - settings.put("draftsFolderSelection", FOLDER_SELECTION_MANUAL); - settings.put("sentFolderSelection", FOLDER_SELECTION_MANUAL); - settings.put("spamFolderSelection", FOLDER_SELECTION_MANUAL); - settings.put("trashFolderSelection", FOLDER_SELECTION_MANUAL); - - return null; - } - } - - /** - * Upgrades settings from version 73 to 74 - * - * Rewrites 'idleRefreshMinutes' from '1' to '2' if necessary - */ - private static class SettingsUpgraderV74 implements SettingsUpgrader { - @Override - public Set upgrade(Map settings) { - Integer idleRefreshMinutes = (Integer) settings.get("idleRefreshMinutes"); - if (idleRefreshMinutes == 1) { - settings.put("idleRefreshMinutes", 2); - } - - return null; - } - } - - /** - * Upgrades settings from version 79 to 80 - * - * Rewrites 'led' and 'lecColor' to 'notificationLight'. - */ - private static class SettingsUpgraderV80 implements SettingsUpgrader { - private final NotificationLightDecoder notificationLightDecoder = DI.get(NotificationLightDecoder.class); - - @Override - public Set upgrade(Map settings) { - Boolean isLedEnabled = (Boolean) settings.get("led"); - Integer ledColor = (Integer) settings.get("ledColor"); - Integer chipColor = (Integer) settings.get("chipColor"); - - if (isLedEnabled != null && ledColor != null) { - int accountColor = chipColor != null ? chipColor : 0; - NotificationLight light = notificationLightDecoder.decode(isLedEnabled, ledColor, accountColor); - settings.put("notificationLight", light.name()); - } - - return SetsKt.setOf("led", "ledColor"); - } - } - - /** - * Rewrite the per-network type IMAP compression settings to a single setting. - */ - private static class SettingsUpgraderV81 implements SettingsUpgrader { - @Override - public Set upgrade(Map settings) { - Boolean useCompressionWifi = (Boolean) settings.get("useCompression.WIFI"); - Boolean useCompressionMobile = (Boolean) settings.get("useCompression.MOBILE"); - Boolean useCompressionOther = (Boolean) settings.get("useCompression.OTHER"); - - boolean useCompression = useCompressionWifi != null && useCompressionMobile != null && - useCompressionOther != null && useCompressionWifi && useCompressionMobile && useCompressionOther; - settings.put("useCompression", useCompression); - - return SetsKt.setOf("useCompression.WIFI", "useCompression.MOBILE", "useCompression.OTHER"); - } - } } diff --git a/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java b/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java index 1f2fb45007..53e5ea3a81 100644 --- a/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java +++ b/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java @@ -3,7 +3,6 @@ package com.fsck.k9.preferences; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -33,6 +32,11 @@ import com.fsck.k9.preferences.Settings.SettingsDescription; import com.fsck.k9.preferences.Settings.SettingsUpgrader; import com.fsck.k9.preferences.Settings.V; import com.fsck.k9.preferences.Settings.WebFontSizeSetting; +import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo24; +import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo31; +import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo58; +import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo69; +import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo79; import static com.fsck.k9.K9.LockScreenNotificationVisibility; @@ -289,11 +293,11 @@ public class GeneralSettingsDescriptions { SETTINGS = Collections.unmodifiableMap(s); Map u = new HashMap<>(); - u.put(24, new SettingsUpgraderV24()); - u.put(31, new SettingsUpgraderV31()); - u.put(58, new SettingsUpgraderV58()); - u.put(69, new SettingsUpgraderV69()); - u.put(79, new SettingsUpgraderV79()); + u.put(24, new GeneralSettingsUpgraderTo24()); + u.put(31, new GeneralSettingsUpgraderTo31()); + u.put(58, new GeneralSettingsUpgraderTo58()); + u.put(69, new GeneralSettingsUpgraderTo69()); + u.put(79, new GeneralSettingsUpgraderTo79()); UPGRADERS = Collections.unmodifiableMap(u); } @@ -321,132 +325,6 @@ public class GeneralSettingsDescriptions { return result; } - /** - * Upgrades the settings from version 23 to 24. - * - *

- * Set messageViewTheme to {@link SubTheme#USE_GLOBAL} if messageViewTheme has - * the same value as theme. - *

- */ - private static class SettingsUpgraderV24 implements SettingsUpgrader { - - @Override - public Set upgrade(Map settings) { - SubTheme messageViewTheme = (SubTheme) settings.get("messageViewTheme"); - AppTheme theme = (AppTheme) settings.get("theme"); - if ((theme == AppTheme.LIGHT && messageViewTheme == SubTheme.LIGHT) || - (theme == AppTheme.DARK && messageViewTheme == SubTheme.DARK)) { - settings.put("messageViewTheme", SubTheme.USE_GLOBAL); - } - - return null; - } - } - - /** - * Upgrades the settings from version 30 to 31. - * - *

- * Convert value from fontSizeMessageViewContent to - * fontSizeMessageViewContentPercent. - *

- */ - public static class SettingsUpgraderV31 implements SettingsUpgrader { - - @Override - public Set upgrade(Map settings) { - int oldSize = (Integer) settings.get("fontSizeMessageViewContent"); - - int newSize = convertFromOldSize(oldSize); - - settings.put("fontSizeMessageViewContentPercent", newSize); - - return new HashSet<>(Collections.singletonList("fontSizeMessageViewContent")); - } - - public static int convertFromOldSize(int oldSize) { - switch (oldSize) { - case 1: { - return 40; - } - case 2: { - return 75; - } - case 4: { - return 175; - } - case 5: { - return 250; - } - case 3: - default: { - return 100; - } - } - } - } - - /** - * Upgrades the settings from version 57 to 58. - * - *

- * Set theme to {@link AppTheme#FOLLOW_SYSTEM} if theme has the value {@link AppTheme#LIGHT}. - *

- */ - private static class SettingsUpgraderV58 implements SettingsUpgrader { - - @Override - public Set upgrade(Map settings) { - AppTheme theme = (AppTheme) settings.get("theme"); - if (theme == AppTheme.LIGHT) { - settings.put("theme", AppTheme.FOLLOW_SYSTEM); - } - - return null; - } - } - - /** - * Upgrades the settings from version 68 to 69. - * - *

- * Renames {@code hideSpecialAccounts} to {@code showUnifiedInbox}. - *

- */ - private static class SettingsUpgraderV69 implements SettingsUpgrader { - - @Override - public Set upgrade(Map settings) { - Boolean hideSpecialAccounts = (Boolean) settings.get("hideSpecialAccounts"); - boolean showUnifiedInbox = hideSpecialAccounts == null || !hideSpecialAccounts; - settings.put("showUnifiedInbox", showUnifiedInbox); - - return new HashSet<>(Collections.singleton("hideSpecialAccounts")); - } - } - - /** - * Upgrades the settings from version 78 to 79. - * - *

- * Change default value of {@code registeredNameColor} to have enough contrast in both the light and dark theme. - *

- */ - private static class SettingsUpgraderV79 implements SettingsUpgrader { - - @Override - public Set upgrade(Map settings) { - final Integer registeredNameColorValue = (Integer) settings.get("registeredNameColor"); - - if (registeredNameColorValue != null && registeredNameColorValue == 0xFF00008F) { - settings.put("registeredNameColor", 0xFF1093F5); - } - - return null; - } - } - private static class LanguageSetting extends PseudoEnumSetting { private final Context context = DI.get(Context.class); private final Map mapping; 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 dcff73e46e..3e617a5ec4 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 @@ -367,7 +367,7 @@ public class Settings { * * @see Settings#upgrade(int, Map, Map, Map) */ - interface SettingsUpgrader { + public interface SettingsUpgrader { /** * Upgrade the provided settings. * diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo53.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo53.java new file mode 100644 index 0000000000..b5eb199b65 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo53.java @@ -0,0 +1,34 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Replace folder entries of "-NONE-" with {@code null}. + */ +public class AccountSettingsUpgraderTo53 implements SettingsUpgrader { + public static final String FOLDER_NONE = "-NONE-"; + + @Override + public Set upgrade(Map settings) { + upgradeFolderEntry(settings, "archiveFolderName"); + upgradeFolderEntry(settings, "autoExpandFolderName"); + upgradeFolderEntry(settings, "draftsFolderName"); + upgradeFolderEntry(settings, "sentFolderName"); + upgradeFolderEntry(settings, "spamFolderName"); + upgradeFolderEntry(settings, "trashFolderName"); + + return null; + } + + private void upgradeFolderEntry(Map settings, String key) { + String archiveFolderName = (String) settings.get(key); + if (FOLDER_NONE.equals(archiveFolderName)) { + settings.put(key, null); + } + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo54.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo54.java new file mode 100644 index 0000000000..0d61fbb7ca --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo54.java @@ -0,0 +1,26 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Inserts folder selection entries with a value of "MANUAL" + */ +public class AccountSettingsUpgraderTo54 implements SettingsUpgrader { + private static final String FOLDER_SELECTION_MANUAL = "MANUAL"; + + @Override + public Set upgrade(Map settings) { + settings.put("archiveFolderSelection", FOLDER_SELECTION_MANUAL); + settings.put("draftsFolderSelection", FOLDER_SELECTION_MANUAL); + settings.put("sentFolderSelection", FOLDER_SELECTION_MANUAL); + settings.put("spamFolderSelection", FOLDER_SELECTION_MANUAL); + settings.put("trashFolderSelection", FOLDER_SELECTION_MANUAL); + + return null; + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo74.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo74.java new file mode 100644 index 0000000000..21adfdc6cc --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo74.java @@ -0,0 +1,23 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Rewrites 'idleRefreshMinutes' from '1' to '2' if necessary + */ +public class AccountSettingsUpgraderTo74 implements SettingsUpgrader { + @Override + public Set upgrade(Map settings) { + Integer idleRefreshMinutes = (Integer) settings.get("idleRefreshMinutes"); + if (idleRefreshMinutes == 1) { + settings.put("idleRefreshMinutes", 2); + } + + return null; + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo80.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo80.java new file mode 100644 index 0000000000..49643a3740 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo80.java @@ -0,0 +1,34 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.DI; +import com.fsck.k9.NotificationLight; +import com.fsck.k9.notification.NotificationLightDecoder; +import com.fsck.k9.preferences.Settings.SettingsUpgrader; +import kotlin.collections.SetsKt; + + +/** + * Rewrites 'led' and 'lecColor' to 'notificationLight'. + */ +public class AccountSettingsUpgraderTo80 implements SettingsUpgrader { + private final NotificationLightDecoder notificationLightDecoder = DI.get(NotificationLightDecoder.class); + + @Override + public Set upgrade(Map settings) { + Boolean isLedEnabled = (Boolean) settings.get("led"); + Integer ledColor = (Integer) settings.get("ledColor"); + Integer chipColor = (Integer) settings.get("chipColor"); + + if (isLedEnabled != null && ledColor != null) { + int accountColor = chipColor != null ? chipColor : 0; + NotificationLight light = notificationLightDecoder.decode(isLedEnabled, ledColor, accountColor); + settings.put("notificationLight", light.name()); + } + + return SetsKt.setOf("led", "ledColor"); + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo81.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo81.java new file mode 100644 index 0000000000..5cff958537 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/AccountSettingsUpgraderTo81.java @@ -0,0 +1,27 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; +import kotlin.collections.SetsKt; + + +/** + * Rewrite the per-network type IMAP compression settings to a single setting. + */ +public class AccountSettingsUpgraderTo81 implements SettingsUpgrader { + @Override + public Set upgrade(Map settings) { + Boolean useCompressionWifi = (Boolean) settings.get("useCompression.WIFI"); + Boolean useCompressionMobile = (Boolean) settings.get("useCompression.MOBILE"); + Boolean useCompressionOther = (Boolean) settings.get("useCompression.OTHER"); + + boolean useCompression = useCompressionWifi != null && useCompressionMobile != null && + useCompressionOther != null && useCompressionWifi && useCompressionMobile && useCompressionOther; + settings.put("useCompression", useCompression); + + return SetsKt.setOf("useCompression.WIFI", "useCompression.MOBILE", "useCompression.OTHER"); + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo24.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo24.java new file mode 100644 index 0000000000..2347eda2b3 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo24.java @@ -0,0 +1,29 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.AppTheme; +import com.fsck.k9.preferences.Settings.SettingsUpgrader; +import com.fsck.k9.preferences.SubTheme; + + +/** + * Set messageViewTheme to {@link SubTheme#USE_GLOBAL} if messageViewTheme has the same value as + * theme. + */ +public class GeneralSettingsUpgraderTo24 implements SettingsUpgrader { + + @Override + public Set upgrade(Map settings) { + SubTheme messageViewTheme = (SubTheme) settings.get("messageViewTheme"); + AppTheme theme = (AppTheme) settings.get("theme"); + if ((theme == AppTheme.LIGHT && messageViewTheme == SubTheme.LIGHT) || + (theme == AppTheme.DARK && messageViewTheme == SubTheme.DARK)) { + settings.put("messageViewTheme", SubTheme.USE_GLOBAL); + } + + return null; + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo31.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo31.java new file mode 100644 index 0000000000..41bc8867a9 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo31.java @@ -0,0 +1,48 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Convert value from fontSizeMessageViewContent to fontSizeMessageViewContentPercent. + */ +public class GeneralSettingsUpgraderTo31 implements SettingsUpgrader { + + @Override + public Set upgrade(Map settings) { + int oldSize = (Integer) settings.get("fontSizeMessageViewContent"); + + int newSize = convertFromOldSize(oldSize); + + settings.put("fontSizeMessageViewContentPercent", newSize); + + return new HashSet<>(Collections.singletonList("fontSizeMessageViewContent")); + } + + public static int convertFromOldSize(int oldSize) { + switch (oldSize) { + case 1: { + return 40; + } + case 2: { + return 75; + } + case 4: { + return 175; + } + case 5: { + return 250; + } + case 3: + default: { + return 100; + } + } + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo58.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo58.java new file mode 100644 index 0000000000..e7f06aff1f --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo58.java @@ -0,0 +1,25 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.AppTheme; +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Set theme to {@link AppTheme#FOLLOW_SYSTEM} if theme has the value {@link AppTheme#LIGHT}. + */ +public class GeneralSettingsUpgraderTo58 implements SettingsUpgrader { + + @Override + public Set upgrade(Map settings) { + AppTheme theme = (AppTheme) settings.get("theme"); + if (theme == AppTheme.LIGHT) { + settings.put("theme", AppTheme.FOLLOW_SYSTEM); + } + + return null; + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo69.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo69.java new file mode 100644 index 0000000000..dd9812d613 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo69.java @@ -0,0 +1,25 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Renames {@code hideSpecialAccounts} to {@code showUnifiedInbox}. + */ +public class GeneralSettingsUpgraderTo69 implements SettingsUpgrader { + + @Override + public Set upgrade(Map settings) { + Boolean hideSpecialAccounts = (Boolean) settings.get("hideSpecialAccounts"); + boolean showUnifiedInbox = hideSpecialAccounts == null || !hideSpecialAccounts; + settings.put("showUnifiedInbox", showUnifiedInbox); + + return new HashSet<>(Collections.singleton("hideSpecialAccounts")); + } +} diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo79.java b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo79.java new file mode 100644 index 0000000000..5338f6feb4 --- /dev/null +++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/GeneralSettingsUpgraderTo79.java @@ -0,0 +1,29 @@ +package com.fsck.k9.preferences.upgrader; + + +import java.util.Map; +import java.util.Set; + +import com.fsck.k9.preferences.Settings.SettingsUpgrader; + + +/** + * Upgrades the settings from version 78 to 79. + * + *

+ * Change default value of {@code registeredNameColor} to have enough contrast in both the light and dark theme. + *

+ */ +public class GeneralSettingsUpgraderTo79 implements SettingsUpgrader { + + @Override + public Set upgrade(Map settings) { + final Integer registeredNameColorValue = (Integer) settings.get("registeredNameColor"); + + if (registeredNameColorValue != null && registeredNameColorValue == 0xFF00008F) { + settings.put("registeredNameColor", 0xFF1093F5); + } + + return null; + } +} diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo11.kt b/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo11.kt index 6492f3ba96..3b1d14b7d4 100644 --- a/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo11.kt +++ b/app/storage/src/main/java/com/fsck/k9/preferences/migrations/StorageMigrationTo11.kt @@ -1,7 +1,7 @@ package com.fsck.k9.preferences.migrations import android.database.sqlite.SQLiteDatabase -import com.fsck.k9.preferences.GeneralSettingsDescriptions +import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo31 /** * Convert old value for message view content font size to new format. @@ -19,7 +19,7 @@ class StorageMigrationTo11( if (newFontSizeValue != null) return val oldFontSizeValue = migrationsHelper.readValue(db, "fontSizeMessageViewContent")?.toIntOrNull() ?: 3 - val fontSizeValue = GeneralSettingsDescriptions.SettingsUpgraderV31.convertFromOldSize(oldFontSizeValue) + val fontSizeValue = GeneralSettingsUpgraderTo31.convertFromOldSize(oldFontSizeValue) migrationsHelper.writeValue(db, "fontSizeMessageViewContentPercent", fontSizeValue.toString()) migrationsHelper.writeValue(db, "fontSizeMessageViewContent", null) } -- GitLab From ba91bfbf91afa9918522628bf8e793929f6e7d2b Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 5 Oct 2023 18:46:43 +0200 Subject: [PATCH 11/11] Add label "unconfirmed" to bug reports and feature requests (cherry picked from commit 89f59b0b5a3b5c3fe28737d868d1a0e68ee85629) --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c44bf97bfc..57f60423a7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug report description: Let us know about crashes or existing functionality not working like it should. -labels: [ "bug" ] +labels: [ "bug", "unconfirmed" ] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 491635265f..88f0b84d1c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,6 @@ name: Feature request description: Suggest an idea for K-9 Mail -labels: [ "enhancement" ] +labels: [ "enhancement", "unconfirmed" ] body: - type: checkboxes id: checklist -- GitLab