Loading feature/migration/qrcode/build.gradle.kts +2 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,8 @@ android { dependencies { implementation(projects.core.common) implementation(projects.legacy.account) implementation(projects.legacy.common) implementation(projects.legacy.ui.base) implementation(projects.core.ui.compose.designsystem) Loading feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/QrCodeModule.kt +6 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,12 @@ val qrCodeModule = module { factory { QrCodePayloadAdapter() } factory { QrCodePayloadParser(qrCodePayloadAdapter = get()) } factory { QrCodePayloadValidator() } factory { QrCodePayloadMapper(qrCodePayloadValidator = get()) } factory { QrCodePayloadMapper( qrCodePayloadValidator = get(), deletePolicyProvider = get(), ) } factory<UseCase.QrCodePayloadReader> { QrCodePayloadReader( Loading feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/domain/entity/AccountData.kt +3 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ package app.k9mail.feature.migration.qrcode.domain.entity import app.k9mail.core.common.mail.EmailAddress import app.k9mail.core.common.net.Hostname import app.k9mail.core.common.net.Port import app.k9mail.legacy.account.Account import app.k9mail.legacy.account.Account.DeletePolicy internal data class AccountData( val sequenceNumber: Int, Loading @@ -11,6 +13,7 @@ internal data class AccountData( ) { data class Account( val accountName: String, val deletePolicy: DeletePolicy, val incomingServer: IncomingServer, val outgoingServerGroups: List<OutgoingServerGroup>, ) Loading feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/payload/QrCodePayloadMapper.kt +16 −0 Original line number Diff line number Diff line package app.k9mail.feature.migration.qrcode.payload import app.k9mail.core.common.mail.Protocols import app.k9mail.core.common.mail.toUserEmailAddress import app.k9mail.core.common.net.toHostname import app.k9mail.core.common.net.toPort import app.k9mail.feature.migration.qrcode.domain.entity.AccountData import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.IncomingServerProtocol import app.k9mail.legacy.account.Account.DeletePolicy import com.fsck.k9.account.DeletePolicyProvider internal class QrCodePayloadMapper( private val qrCodePayloadValidator: QrCodePayloadValidator, private val deletePolicyProvider: DeletePolicyProvider, ) { fun toAccountData(data: QrCodeData): AccountData? { return if (qrCodePayloadValidator.isValid(data)) { Loading @@ -31,9 +36,11 @@ internal class QrCodePayloadMapper( accountName = account.incomingServer.accountName, identity = outgoingServerGroups.first().identities.first(), ) val deletePolicy = getDeletePolicy(incomingServer.protocol) return AccountData.Account( accountName = accountName, deletePolicy = deletePolicy, incomingServer = incomingServer, outgoingServerGroups = outgoingServerGroups, ) Loading Loading @@ -92,4 +99,13 @@ internal class QrCodePayloadMapper( displayName = identity.displayName, ) } private fun getDeletePolicy(protocol: IncomingServerProtocol): DeletePolicy { val accountType = when (protocol) { IncomingServerProtocol.Imap -> Protocols.IMAP IncomingServerProtocol.Pop3 -> Protocols.POP3 } return deletePolicyProvider.getDeletePolicy(accountType) } } feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/settings/XmlSettingWriter.kt +30 −0 Original line number Diff line number Diff line Loading @@ -6,11 +6,13 @@ import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.Identity import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.IncomingServer import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.OutgoingServer import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.OutgoingServerGroup import app.k9mail.legacy.account.Account.DeletePolicy import java.io.OutputStream import org.xmlpull.v1.XmlSerializer // TODO: This duplicates much of the code in SettingsExporter. Add an abstraction layer for the input data, so we can // use a single XML writer class for exporting accounts and writing QR code payloads to a settings file. @Suppress("TooManyFunctions") internal class XmlSettingWriter( private val uuidGenerator: UuidGenerator, ) { Loading Loading @@ -56,12 +58,28 @@ internal class XmlSettingWriter( attribute(null, UUID_ATTRIBUTE, accountUuid) writeElement(NAME_ELEMENT, account.accountName) writeSettings(account) writeIncomingServer(account.incomingServer) writeOutgoingServers(account.outgoingServerGroups) endTag(null, ACCOUNT_ELEMENT) } private fun XmlSerializer.writeSettings(account: Account) { startTag(null, SETTINGS_ELEMENT) writeKeyValue("deletePolicy", account.deletePolicy.toSettingsFileValue()) endTag(null, SETTINGS_ELEMENT) } private fun XmlSerializer.writeKeyValue(key: String, value: String?) { startTag(null, VALUE_ELEMENT) attribute(null, KEY_ATTRIBUTE, key) if (value != null) { text(value) } endTag(null, VALUE_ELEMENT) } private fun XmlSerializer.writeIncomingServer(incomingServer: IncomingServer) { startTag(null, INCOMING_SERVER_ELEMENT) attribute(null, TYPE_ATTRIBUTE, incomingServer.protocol.mapToSettingsString()) Loading Loading @@ -139,6 +157,9 @@ internal class XmlSettingWriter( private const val ACCOUNTS_ELEMENT = "accounts" private const val ACCOUNT_ELEMENT = "account" private const val UUID_ATTRIBUTE = "uuid" private const val SETTINGS_ELEMENT = "settings" private const val VALUE_ELEMENT = "value" private const val KEY_ATTRIBUTE = "key" private const val INCOMING_SERVER_ELEMENT = "incoming-server" private const val OUTGOING_SERVER_ELEMENT = "outgoing-server" private const val TYPE_ATTRIBUTE = "type" Loading @@ -154,3 +175,12 @@ internal class XmlSettingWriter( private const val EMAIL_ELEMENT = "email" } } private fun DeletePolicy.toSettingsFileValue(): String { return when (this) { DeletePolicy.NEVER -> "NEVER" DeletePolicy.SEVEN_DAYS -> error("Unsupported value") DeletePolicy.ON_DELETE -> "DELETE" DeletePolicy.MARK_AS_READ -> "MARK_AS_READ" } } Loading
feature/migration/qrcode/build.gradle.kts +2 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,8 @@ android { dependencies { implementation(projects.core.common) implementation(projects.legacy.account) implementation(projects.legacy.common) implementation(projects.legacy.ui.base) implementation(projects.core.ui.compose.designsystem) Loading
feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/QrCodeModule.kt +6 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,12 @@ val qrCodeModule = module { factory { QrCodePayloadAdapter() } factory { QrCodePayloadParser(qrCodePayloadAdapter = get()) } factory { QrCodePayloadValidator() } factory { QrCodePayloadMapper(qrCodePayloadValidator = get()) } factory { QrCodePayloadMapper( qrCodePayloadValidator = get(), deletePolicyProvider = get(), ) } factory<UseCase.QrCodePayloadReader> { QrCodePayloadReader( Loading
feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/domain/entity/AccountData.kt +3 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ package app.k9mail.feature.migration.qrcode.domain.entity import app.k9mail.core.common.mail.EmailAddress import app.k9mail.core.common.net.Hostname import app.k9mail.core.common.net.Port import app.k9mail.legacy.account.Account import app.k9mail.legacy.account.Account.DeletePolicy internal data class AccountData( val sequenceNumber: Int, Loading @@ -11,6 +13,7 @@ internal data class AccountData( ) { data class Account( val accountName: String, val deletePolicy: DeletePolicy, val incomingServer: IncomingServer, val outgoingServerGroups: List<OutgoingServerGroup>, ) Loading
feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/payload/QrCodePayloadMapper.kt +16 −0 Original line number Diff line number Diff line package app.k9mail.feature.migration.qrcode.payload import app.k9mail.core.common.mail.Protocols import app.k9mail.core.common.mail.toUserEmailAddress import app.k9mail.core.common.net.toHostname import app.k9mail.core.common.net.toPort import app.k9mail.feature.migration.qrcode.domain.entity.AccountData import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.IncomingServerProtocol import app.k9mail.legacy.account.Account.DeletePolicy import com.fsck.k9.account.DeletePolicyProvider internal class QrCodePayloadMapper( private val qrCodePayloadValidator: QrCodePayloadValidator, private val deletePolicyProvider: DeletePolicyProvider, ) { fun toAccountData(data: QrCodeData): AccountData? { return if (qrCodePayloadValidator.isValid(data)) { Loading @@ -31,9 +36,11 @@ internal class QrCodePayloadMapper( accountName = account.incomingServer.accountName, identity = outgoingServerGroups.first().identities.first(), ) val deletePolicy = getDeletePolicy(incomingServer.protocol) return AccountData.Account( accountName = accountName, deletePolicy = deletePolicy, incomingServer = incomingServer, outgoingServerGroups = outgoingServerGroups, ) Loading Loading @@ -92,4 +99,13 @@ internal class QrCodePayloadMapper( displayName = identity.displayName, ) } private fun getDeletePolicy(protocol: IncomingServerProtocol): DeletePolicy { val accountType = when (protocol) { IncomingServerProtocol.Imap -> Protocols.IMAP IncomingServerProtocol.Pop3 -> Protocols.POP3 } return deletePolicyProvider.getDeletePolicy(accountType) } }
feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/settings/XmlSettingWriter.kt +30 −0 Original line number Diff line number Diff line Loading @@ -6,11 +6,13 @@ import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.Identity import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.IncomingServer import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.OutgoingServer import app.k9mail.feature.migration.qrcode.domain.entity.AccountData.OutgoingServerGroup import app.k9mail.legacy.account.Account.DeletePolicy import java.io.OutputStream import org.xmlpull.v1.XmlSerializer // TODO: This duplicates much of the code in SettingsExporter. Add an abstraction layer for the input data, so we can // use a single XML writer class for exporting accounts and writing QR code payloads to a settings file. @Suppress("TooManyFunctions") internal class XmlSettingWriter( private val uuidGenerator: UuidGenerator, ) { Loading Loading @@ -56,12 +58,28 @@ internal class XmlSettingWriter( attribute(null, UUID_ATTRIBUTE, accountUuid) writeElement(NAME_ELEMENT, account.accountName) writeSettings(account) writeIncomingServer(account.incomingServer) writeOutgoingServers(account.outgoingServerGroups) endTag(null, ACCOUNT_ELEMENT) } private fun XmlSerializer.writeSettings(account: Account) { startTag(null, SETTINGS_ELEMENT) writeKeyValue("deletePolicy", account.deletePolicy.toSettingsFileValue()) endTag(null, SETTINGS_ELEMENT) } private fun XmlSerializer.writeKeyValue(key: String, value: String?) { startTag(null, VALUE_ELEMENT) attribute(null, KEY_ATTRIBUTE, key) if (value != null) { text(value) } endTag(null, VALUE_ELEMENT) } private fun XmlSerializer.writeIncomingServer(incomingServer: IncomingServer) { startTag(null, INCOMING_SERVER_ELEMENT) attribute(null, TYPE_ATTRIBUTE, incomingServer.protocol.mapToSettingsString()) Loading Loading @@ -139,6 +157,9 @@ internal class XmlSettingWriter( private const val ACCOUNTS_ELEMENT = "accounts" private const val ACCOUNT_ELEMENT = "account" private const val UUID_ATTRIBUTE = "uuid" private const val SETTINGS_ELEMENT = "settings" private const val VALUE_ELEMENT = "value" private const val KEY_ATTRIBUTE = "key" private const val INCOMING_SERVER_ELEMENT = "incoming-server" private const val OUTGOING_SERVER_ELEMENT = "outgoing-server" private const val TYPE_ATTRIBUTE = "type" Loading @@ -154,3 +175,12 @@ internal class XmlSettingWriter( private const val EMAIL_ELEMENT = "email" } } private fun DeletePolicy.toSettingsFileValue(): String { return when (this) { DeletePolicy.NEVER -> "NEVER" DeletePolicy.SEVEN_DAYS -> error("Unsupported value") DeletePolicy.ON_DELETE -> "DELETE" DeletePolicy.MARK_AS_READ -> "MARK_AS_READ" } }