diff --git a/app-common/build.gradle.kts b/app-common/build.gradle.kts index 871afb85bca..4bcba76fb7c 100644 --- a/app-common/build.gradle.kts +++ b/app-common/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.feature.widget.messageList) implementation(projects.mail.protocols.imap) + implementation(projects.backend.imap) implementation(libs.androidx.work.runtime) implementation(libs.androidx.lifecycle.process) diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt index 3d73a285f7a..eb9793c8c92 100644 --- a/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt @@ -2,10 +2,10 @@ package net.thunderbird.app.common.account import android.content.res.Resources import app.k9mail.core.ui.legacy.theme2.common.R -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager internal class AccountColorPicker( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val resources: Resources, ) { fun pickColor(): Int { diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/account/AppCommonAccountModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AppCommonAccountModule.kt index 0bf88d0044b..610f2263df9 100644 --- a/app-common/src/main/kotlin/net/thunderbird/app/common/account/AppCommonAccountModule.kt +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AppCommonAccountModule.kt @@ -4,13 +4,16 @@ import app.k9mail.feature.account.setup.AccountSetupExternalContract import net.thunderbird.app.common.account.data.DefaultAccountProfileLocalDataSource import net.thunderbird.app.common.account.data.DefaultLegacyAccountManager import net.thunderbird.core.android.account.AccountDefaultsProvider +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.feature.account.avatar.AvatarMonogramCreator import net.thunderbird.feature.account.avatar.DefaultAvatarMonogramCreator import net.thunderbird.feature.account.core.AccountCoreExternalContract.AccountProfileLocalDataSource import net.thunderbird.feature.account.core.featureAccountCoreModule import net.thunderbird.feature.account.storage.legacy.featureAccountStorageLegacyModule +import net.thunderbird.feature.mail.account.api.AccountManager import org.koin.android.ext.koin.androidApplication +import org.koin.dsl.binds import org.koin.dsl.module internal val appCommonAccountModule = module { @@ -19,12 +22,12 @@ internal val appCommonAccountModule = module { featureAccountStorageLegacyModule, ) - single { + single> { DefaultLegacyAccountManager( accountManager = get(), accountDataMapper = get(), ) - } + } binds arrayOf(LegacyAccountManager::class) single { DefaultAccountProfileLocalDataSource( diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/DefaultLegacyAccountManager.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/DefaultLegacyAccountManager.kt index 0d8b1a3aacc..bdc213790d1 100644 --- a/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/DefaultLegacyAccountManager.kt +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/DefaultLegacyAccountManager.kt @@ -2,15 +2,15 @@ package net.thunderbird.app.common.account.data import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.feature.account.AccountId -import net.thunderbird.feature.account.storage.legacy.mapper.DefaultLegacyAccountWrapperDataMapper +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper internal class DefaultLegacyAccountManager( - private val accountManager: AccountManager, - private val accountDataMapper: DefaultLegacyAccountWrapperDataMapper, + private val accountManager: LegacyAccountDtoManager, + private val accountDataMapper: LegacyAccountDataMapper, ) : LegacyAccountManager { override fun getAll(): Flow> { @@ -35,4 +35,34 @@ internal class DefaultLegacyAccountManager( accountDataMapper.toDto(account), ) } + + override fun getAccounts(): List { + return accountManager.getAccounts() + .map { account -> + accountDataMapper.toDomain(account) + } + } + + override fun getAccountsFlow(): Flow> = getAll() + + override fun getAccount(accountUuid: String): LegacyAccount? { + val dto = accountManager.getAccount(accountUuid) + return dto?.let { accountDataMapper.toDomain(it) } + } + + override fun getAccountFlow(accountUuid: String): Flow { + return accountManager.getAccountFlow(accountUuid).map { account -> + account?.let { + accountDataMapper.toDomain(it) + } + } + } + + override fun moveAccount(account: LegacyAccount, newPosition: Int) { + accountManager.moveAccount(accountDataMapper.toDto(account), newPosition) + } + + override fun saveAccount(account: LegacyAccount) { + accountManager.saveAccount(accountDataMapper.toDto(account)) + } } diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt index ddc8e2ae0b5..f43927d5215 100644 --- a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt @@ -2,6 +2,7 @@ package net.thunderbird.app.common.feature import app.k9mail.feature.launcher.FeatureLauncherExternalContract import app.k9mail.feature.launcher.di.featureLauncherModule +import net.thunderbird.app.common.feature.mail.appCommonFeatureMailModule import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract import net.thunderbird.feature.notification.impl.inject.featureNotificationModule import org.koin.android.ext.koin.androidContext @@ -10,6 +11,7 @@ import org.koin.dsl.module internal val appCommonFeatureModule = module { includes(featureLauncherModule) includes(featureNotificationModule) + includes(appCommonFeatureMailModule) factory { AccountSetupFinishedLauncher( diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountBackendStorageFactory.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountBackendStorageFactory.kt new file mode 100644 index 00000000000..e62bc9fdab0 --- /dev/null +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountBackendStorageFactory.kt @@ -0,0 +1,28 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.backend.api.BackendStorage +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory +import net.thunderbird.backend.api.BackendStorageFactory +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper +import net.thunderbird.feature.mail.account.api.BaseAccount + +/** + * A [BackendStorageFactory] that supports both [LegacyAccountDto] and [LegacyAccount]. + */ +class BaseAccountBackendStorageFactory( + private val legacyFactory: LegacyAccountDtoBackendStorageFactory, + private val legacyMapper: LegacyAccountDataMapper, +) : BackendStorageFactory { + override fun createBackendStorage(account: BaseAccount): BackendStorage { + return when (account) { + is LegacyAccountDto -> legacyFactory.createBackendStorage(account) + is LegacyAccount -> { + val legacyAccountDto = legacyMapper.toDto(account) + legacyFactory.createBackendStorage(legacyAccountDto) + } + else -> throw IllegalArgumentException("Unsupported account type: ${account::class.java.name}") + } + } +} diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountImapBackendFactory.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountImapBackendFactory.kt new file mode 100644 index 00000000000..56d91fc46d2 --- /dev/null +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountImapBackendFactory.kt @@ -0,0 +1,30 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.backend.api.Backend +import com.fsck.k9.backends.ImapBackendFactory +import net.thunderbird.backend.api.BackendFactory +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.common.mail.Protocols +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper +import net.thunderbird.feature.mail.account.api.BaseAccount + +/** + * A [BackendFactory] that supports both [LegacyAccountDto] and [LegacyAccount]. + */ +class BaseAccountImapBackendFactory( + private val legacyFactory: ImapBackendFactory, + private val legacyMapper: LegacyAccountDataMapper, +) : BackendFactory { + override fun createBackend(account: BaseAccount): Backend { + val dto = when (account) { + is LegacyAccountDto -> account + is LegacyAccount -> legacyMapper.toDto(account) + else -> error("Unsupported account type ${account::class.qualifiedName}") + } + require(dto.incomingServerSettings.type == Protocols.IMAP) { + "IMAP backend requested for non‑IMAP account (id=${dto.id}, type=${dto.incomingServerSettings.type})" + } + return legacyFactory.createBackend(dto) + } +} diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountSpecialFolderUpdaterFactory.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountSpecialFolderUpdaterFactory.kt new file mode 100644 index 00000000000..0d87945ab8e --- /dev/null +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountSpecialFolderUpdaterFactory.kt @@ -0,0 +1,28 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.mailstore.LegacyAccountDtoSpecialFolderUpdaterFactory +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper +import net.thunderbird.feature.mail.account.api.BaseAccount +import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater + +/** + * A [SpecialFolderUpdater.Factory] that supports both [LegacyAccountDto] and [LegacyAccount]. + */ +class BaseAccountSpecialFolderUpdaterFactory( + private val legacyFactory: LegacyAccountDtoSpecialFolderUpdaterFactory, + private val legacyMapper: LegacyAccountDataMapper, +) : SpecialFolderUpdater.Factory { + override fun create(account: BaseAccount): SpecialFolderUpdater { + return when (account) { + is LegacyAccountDto -> legacyFactory.create(account) + is LegacyAccount -> { + val legacyAccountDto = legacyMapper.toDto(account) + legacyFactory.create(legacyAccountDto) + } + + else -> throw IllegalArgumentException("Unsupported account type: ${account::class.java.name}") + } + } +} diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt new file mode 100644 index 00000000000..5ccb4b8b5b9 --- /dev/null +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt @@ -0,0 +1,57 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.mailstore.DefaultSpecialFolderUpdater +import com.fsck.k9.mailstore.LegacyAccountDtoSpecialFolderUpdaterFactory +import net.thunderbird.backend.api.BackendFactory +import net.thunderbird.backend.api.BackendStorageFactory +import net.thunderbird.backend.api.folder.RemoteFolderCreator +import net.thunderbird.backend.imap.DefaultImapRemoteFolderCreatorFactory +import net.thunderbird.backend.imap.ImapRemoteFolderCreatorFactory +import net.thunderbird.feature.mail.account.api.BaseAccount +import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater +import org.koin.dsl.module + +internal val appCommonFeatureMailModule = module { + + single> { + BaseAccountBackendStorageFactory( + legacyFactory = get(), + legacyMapper = get(), + ) + } + + factory { + DefaultSpecialFolderUpdater.Factory( + folderRepository = get(), + specialFolderSelectionStrategy = get(), + preferences = get(), + ) + } + + factory> { + BaseAccountSpecialFolderUpdaterFactory( + legacyFactory = get(), + legacyMapper = get(), + ) + } + + single> { + BaseAccountImapBackendFactory( + legacyFactory = get(), + legacyMapper = get(), + ) + } + + single { + DefaultImapRemoteFolderCreatorFactory( + logger = get(), + backendFactory = get(), + ) + } + + single { + RemoteFolderCreatorResolver( + imapFactory = get(), + ) + } +} diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/NoOpRemoteFolderCreator.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/NoOpRemoteFolderCreator.kt new file mode 100644 index 00000000000..75f0a827ba4 --- /dev/null +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/NoOpRemoteFolderCreator.kt @@ -0,0 +1,20 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.mail.FolderType +import com.fsck.k9.mail.folders.FolderServerId +import net.thunderbird.backend.api.folder.RemoteFolderCreationOutcome +import net.thunderbird.backend.api.folder.RemoteFolderCreator +import net.thunderbird.core.outcome.Outcome + +/** + * A [RemoteFolderCreator] that does nothing and always returns [RemoteFolderCreationOutcome.Success.AlreadyExists]. + */ +object NoOpRemoteFolderCreator : RemoteFolderCreator { + override suspend fun create( + folderServerId: FolderServerId, + mustCreate: Boolean, + folderType: FolderType, + ): Outcome { + return Outcome.success(RemoteFolderCreationOutcome.Success.AlreadyExists) + } +} diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/RemoteFolderCreatorResolver.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/RemoteFolderCreatorResolver.kt new file mode 100644 index 00000000000..b040eb44d4a --- /dev/null +++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/RemoteFolderCreatorResolver.kt @@ -0,0 +1,29 @@ +package net.thunderbird.app.common.feature.mail + +import net.thunderbird.backend.api.folder.RemoteFolderCreator +import net.thunderbird.backend.imap.ImapRemoteFolderCreatorFactory +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.common.mail.Protocols +import net.thunderbird.feature.mail.account.api.BaseAccount + +/** + * Resolves the correct [RemoteFolderCreator] implementation based on the [BaseAccount] type. + */ +class RemoteFolderCreatorResolver( + private val imapFactory: ImapRemoteFolderCreatorFactory, +) : RemoteFolderCreator.Factory { + override fun create(account: BaseAccount): RemoteFolderCreator { + return when (account) { + is LegacyAccountDto -> when (account.incomingServerSettings.type) { + Protocols.IMAP -> imapFactory.create(account) + else -> NoOpRemoteFolderCreator + } + is LegacyAccount -> when (account.incomingServerSettings.type) { + Protocols.IMAP -> imapFactory.create(account) + else -> NoOpRemoteFolderCreator + } + else -> NoOpRemoteFolderCreator + } + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/account/data/FakeLegacyAccountManager.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/account/data/FakeLegacyAccountManager.kt index 3123f215558..abf87507d3b 100644 --- a/app-common/src/test/kotlin/net/thunderbird/app/common/account/data/FakeLegacyAccountManager.kt +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/account/data/FakeLegacyAccountManager.kt @@ -33,4 +33,28 @@ internal class FakeLegacyAccountManager( } } } + + override fun getAccounts(): List { + TODO("Not yet implemented") + } + + override fun getAccountsFlow(): Flow> { + TODO("Not yet implemented") + } + + override fun getAccount(accountUuid: String): LegacyAccount? { + TODO("Not yet implemented") + } + + override fun getAccountFlow(accountUuid: String): Flow { + TODO("Not yet implemented") + } + + override fun moveAccount(account: LegacyAccount, newPosition: Int) { + TODO("Not yet implemented") + } + + override fun saveAccount(account: LegacyAccount) { + TODO("Not yet implemented") + } } diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountBackendStorageFactoryTest.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountBackendStorageFactoryTest.kt new file mode 100644 index 00000000000..419269f72a4 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountBackendStorageFactoryTest.kt @@ -0,0 +1,56 @@ +package net.thunderbird.app.common.feature.mail + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.isInstanceOf +import assertk.assertions.isSameInstanceAs +import net.thunderbird.feature.mail.account.api.BaseAccount +import org.junit.Test + +class BaseAccountBackendStorageFactoryTest { + + @Test + fun `delegates to legacy factory for LegacyAccountDto`() { + // Arrange + val dto = FakeData.legacyAccountDto + val legacyFactory = FakeLegacyAccountDtoBackendStorageFactory() + val mapper = FakeLegacyAccountDataMapper() + val factory = BaseAccountBackendStorageFactory(legacyFactory, mapper) + + // Act + factory.createBackendStorage(dto) + + // Assert + assertThat(legacyFactory.lastAccount).isSameInstanceAs(dto) + } + + @Test + fun `maps LegacyAccount to dto and delegates`() { + // Arrange + val dto = FakeData.legacyAccountDto + val domain = FakeData.legacyAccount + val mapper = FakeLegacyAccountDataMapper().apply { toDtoResult = dto } + val legacyFactory = FakeLegacyAccountDtoBackendStorageFactory() + val factory = BaseAccountBackendStorageFactory(legacyFactory, mapper) + + // Act + factory.createBackendStorage(domain) + + // Assert + assertThat(mapper.lastMapped).isSameInstanceAs(domain) + assertThat(legacyFactory.lastAccount).isSameInstanceAs(dto) + } + + @Test + fun `unsupported BaseAccount throws`() { + // Arrange + val legacyFactory = FakeLegacyAccountDtoBackendStorageFactory() + val mapper = FakeLegacyAccountDataMapper() + val factory = BaseAccountBackendStorageFactory(legacyFactory, mapper) + val unsupported: BaseAccount = FakeData.unsupportedAccount + + // Act & Assert + assertFailure { factory.createBackendStorage(unsupported) } + .isInstanceOf(IllegalArgumentException::class) + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountImapBackendFactoryTest.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountImapBackendFactoryTest.kt new file mode 100644 index 00000000000..88bb2c844f5 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountImapBackendFactoryTest.kt @@ -0,0 +1,110 @@ +package net.thunderbird.app.common.feature.mail + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.isInstanceOf +import assertk.assertions.isSameInstanceAs +import com.fsck.k9.mail.AuthType +import com.fsck.k9.mail.ConnectionSecurity +import com.fsck.k9.mail.ServerSettings +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.common.mail.Protocols +import org.junit.Test + +class BaseAccountImapBackendFactoryTest { + + @Test + fun `imap dto delegates to legacy factory`() { + // Arrange + val account = createLegacyAccountDto(protocol = Protocols.IMAP) + val legacyFactory = FakeLegacyBackendFactory() + val mapper = FakeLegacyAccountDataMapper().apply { toDtoResult = account } + val factory = BaseAccountImapBackendFactory(legacyFactory, mapper) + + // Act + factory.createBackend(account) + + // Assert + assertThat(legacyFactory.lastAccount).isSameInstanceAs(account) + } + + @Test + fun `imap domain account is mapped to dto then delegated`() { + // Arrange + val dto = createLegacyAccountDto(protocol = Protocols.IMAP) + val domain = createLegacyAccountFromDto(dto) + val mapper = FakeLegacyAccountDataMapper().apply { toDtoResult = dto } + val legacyFactory = FakeLegacyBackendFactory() + val factory = BaseAccountImapBackendFactory(legacyFactory, mapper) + + // Act + factory.createBackend(domain) + + // Assert + assertThat(mapper.lastMapped).isSameInstanceAs(domain) + assertThat(legacyFactory.lastAccount).isSameInstanceAs(dto) + } + + @Test + fun `pop3 account throws IllegalArgumentException`() { + // Arrange + val account = createLegacyAccountDto(protocol = Protocols.POP3) + val mapper = FakeLegacyAccountDataMapper().apply { toDtoResult = account } + val legacyFactory = FakeLegacyBackendFactory() + val factory = BaseAccountImapBackendFactory(legacyFactory, mapper) + + // Act + Assert + assertFailure { factory.createBackend(account) } + .isInstanceOf(IllegalArgumentException::class) + } + + private fun createLegacyAccountFromDto(dto: LegacyAccountDto): LegacyAccount { + // Arrange helper: Build a minimal domain account based on dto values + return LegacyAccount( + id = dto.id, + name = dto.name, + profile = net.thunderbird.feature.account.storage.profile.ProfileDto( + id = dto.id, + name = dto.displayName, + color = dto.chipColor, + avatar = dto.avatar, + ), + identities = dto.identities, + email = dto.email, + deletePolicy = dto.deletePolicy, + incomingServerSettings = dto.incomingServerSettings, + outgoingServerSettings = dto.outgoingServerSettings, + ) + } + + private fun createLegacyAccountDto(protocol: String): LegacyAccountDto { + // Arrange helper + val account = LegacyAccountDto(uuid = net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID_RAW) + // Initialize identities to allow email setter usage + account.identities = mutableListOf(net.thunderbird.core.android.account.Identity(email = "user@example.com")) + // Ensure email property is set via identity[0] + account.email = "user@example.com" + account.incomingServerSettings = ServerSettings( + type = protocol, + host = "imap.example.com", + port = 993, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ) + account.outgoingServerSettings = ServerSettings( + type = protocol, + host = "smtp.example.com", + port = 465, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ) + return account + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountSpecialFolderUpdaterFactoryTest.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountSpecialFolderUpdaterFactoryTest.kt new file mode 100644 index 00000000000..e6e78de0f1c --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/BaseAccountSpecialFolderUpdaterFactoryTest.kt @@ -0,0 +1,108 @@ +package net.thunderbird.app.common.feature.mail + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.isInstanceOf +import assertk.assertions.isSameInstanceAs +import com.fsck.k9.mail.AuthType +import com.fsck.k9.mail.ConnectionSecurity +import com.fsck.k9.mail.ServerSettings +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.feature.mail.account.api.BaseAccount +import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater +import org.junit.Test + +class BaseAccountSpecialFolderUpdaterFactoryTest { + + @Test + fun `delegates to legacy factory for LegacyAccountDto`() { + // Arrange + val dto = createLegacyAccountDto() + val legacyFactory = FakeLegacyAccountDtoSpecialFolderUpdaterFactory() + val mapper = FakeLegacyAccountDataMapper().apply { toDtoResult = dto } + val factory = BaseAccountSpecialFolderUpdaterFactory(legacyFactory, mapper) + + // Act + val updater = factory.create(dto) + + // Assert + assertThat(legacyFactory.lastAccount).isSameInstanceAs(dto) + assertThat(updater).isInstanceOf(SpecialFolderUpdater::class) + } + + @Test + fun `maps LegacyAccount to dto and delegates`() { + // Arrange + val dto = createLegacyAccountDto() + val domain = createLegacyAccountFromDto(dto) + val legacyFactory = FakeLegacyAccountDtoSpecialFolderUpdaterFactory() + val mapper = FakeLegacyAccountDataMapper().apply { toDtoResult = dto } + val factory = BaseAccountSpecialFolderUpdaterFactory(legacyFactory, mapper) + + // Act + factory.create(domain) + + // Assert + assertThat(mapper.lastMapped).isSameInstanceAs(domain) + assertThat(legacyFactory.lastAccount).isSameInstanceAs(dto) + } + + @Test + fun `unsupported BaseAccount throws`() { + // Arrange + val unsupported: BaseAccount = FakeData.unsupportedAccount + val legacyFactory = FakeLegacyAccountDtoSpecialFolderUpdaterFactory() + val mapper = FakeLegacyAccountDataMapper() + val factory = BaseAccountSpecialFolderUpdaterFactory(legacyFactory, mapper) + + // Act + Assert + assertFailure { factory.create(unsupported) } + .isInstanceOf(IllegalArgumentException::class) + } + + private fun createLegacyAccountDto(): LegacyAccountDto { + val dto = LegacyAccountDto(uuid = net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID_RAW) + dto.identities = mutableListOf(net.thunderbird.core.android.account.Identity(email = "user@example.com")) + dto.email = "user@example.com" + dto.incomingServerSettings = ServerSettings( + type = net.thunderbird.core.common.mail.Protocols.IMAP, + host = "imap.example.com", + port = 993, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ) + dto.outgoingServerSettings = ServerSettings( + type = net.thunderbird.core.common.mail.Protocols.IMAP, + host = "smtp.example.com", + port = 465, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ) + return dto + } + + private fun createLegacyAccountFromDto(dto: LegacyAccountDto): LegacyAccount { + return LegacyAccount( + id = dto.id, + name = dto.name, + profile = net.thunderbird.feature.account.storage.profile.ProfileDto( + id = dto.id, + name = dto.displayName, + color = dto.chipColor, + avatar = dto.avatar, + ), + identities = dto.identities, + email = dto.email, + deletePolicy = dto.deletePolicy, + incomingServerSettings = dto.incomingServerSettings, + outgoingServerSettings = dto.outgoingServerSettings, + ) + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeData.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeData.kt new file mode 100644 index 00000000000..08dcd92319b --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeData.kt @@ -0,0 +1,88 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.mail.AuthType +import com.fsck.k9.mail.ConnectionSecurity +import com.fsck.k9.mail.ServerSettings +import net.thunderbird.account.fake.FakeAccountData +import net.thunderbird.core.android.account.DeletePolicy +import net.thunderbird.core.android.account.Identity +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.common.mail.Protocols +import net.thunderbird.feature.account.storage.profile.AvatarDto +import net.thunderbird.feature.account.storage.profile.AvatarTypeDto +import net.thunderbird.feature.account.storage.profile.ProfileDto +import net.thunderbird.feature.mail.account.api.BaseAccount + +object FakeData { + + val legacyAccountDto = LegacyAccountDto(uuid = FakeAccountData.ACCOUNT_ID_RAW).apply { + identities = mutableListOf(Identity(email = "user@example.com")) + email = "user@example.com" + incomingServerSettings = ServerSettings( + type = Protocols.IMAP, + host = "imap.example.com", + port = 993, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ) + outgoingServerSettings = ServerSettings( + type = Protocols.IMAP, + host = "smtp.example.com", + port = 465, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ) + } + + val legacyAccount = LegacyAccount( + id = FakeAccountData.ACCOUNT_ID, + name = "user@example.com", + profile = ProfileDto( + id = FakeAccountData.ACCOUNT_ID, + name = "user@example.com", + color = 0, + avatar = AvatarDto( + avatarType = AvatarTypeDto.MONOGRAM, + avatarMonogram = "us", + avatarImageUri = null, + avatarIconName = null, + ), + ), + identities = listOf(Identity(email = "user@example.com")), + email = "user@example.com", + deletePolicy = DeletePolicy.NEVER, + incomingServerSettings = ServerSettings( + type = Protocols.IMAP, + host = "imap.example.com", + port = 993, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ), + outgoingServerSettings = ServerSettings( + type = Protocols.IMAP, + host = "smtp.example.com", + port = 465, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "user", + password = "pass", + clientCertificateAlias = null, + ), + ) + + val unsupportedAccount = object : BaseAccount { + override val uuid: String = "x" + override val name: String? = "n" + override val email: String = "e@example.com" + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeImapRemoteFolderCreatorFactory.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeImapRemoteFolderCreatorFactory.kt new file mode 100644 index 00000000000..f0b44129747 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeImapRemoteFolderCreatorFactory.kt @@ -0,0 +1,24 @@ +package net.thunderbird.app.common.feature.mail + +import net.thunderbird.backend.api.folder.RemoteFolderCreationOutcome +import net.thunderbird.backend.api.folder.RemoteFolderCreator +import net.thunderbird.backend.imap.ImapRemoteFolderCreatorFactory +import net.thunderbird.core.outcome.Outcome +import net.thunderbird.feature.mail.account.api.BaseAccount + +class FakeImapRemoteFolderCreatorFactory : ImapRemoteFolderCreatorFactory { + + var lastAccount: BaseAccount? = null + + override fun create(account: BaseAccount): RemoteFolderCreator { + lastAccount = account + + return object : RemoteFolderCreator { + override suspend fun create( + folderServerId: com.fsck.k9.mail.folders.FolderServerId, + mustCreate: Boolean, + folderType: com.fsck.k9.mail.FolderType, + ) = Outcome.success(RemoteFolderCreationOutcome.Success.AlreadyExists) + } + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDataMapper.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDataMapper.kt new file mode 100644 index 00000000000..7e37821e029 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDataMapper.kt @@ -0,0 +1,16 @@ +package net.thunderbird.app.common.feature.mail + +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper + +internal class FakeLegacyAccountDataMapper : LegacyAccountDataMapper { + var lastMapped: LegacyAccount? = null + var toDtoResult: LegacyAccountDto? = null + + override fun toDomain(dto: LegacyAccountDto): LegacyAccount = throw UnsupportedOperationException() + override fun toDto(domain: LegacyAccount): LegacyAccountDto { + lastMapped = domain + return toDtoResult ?: error("toDtoResult must be set") + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDtoBackendStorageFactory.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDtoBackendStorageFactory.kt new file mode 100644 index 00000000000..47289b062fa --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDtoBackendStorageFactory.kt @@ -0,0 +1,34 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.backend.api.BackendFolderUpdater +import com.fsck.k9.backend.api.BackendStorage +import com.fsck.k9.backend.api.FolderInfo +import com.fsck.k9.mail.FolderType +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory +import net.thunderbird.core.android.account.LegacyAccountDto + +internal class FakeLegacyAccountDtoBackendStorageFactory : LegacyAccountDtoBackendStorageFactory { + var lastAccount: LegacyAccountDto? = null + + override fun createBackendStorage(account: LegacyAccountDto): BackendStorage { + lastAccount = account + return object : BackendStorage { + override fun getFolder(folderServerId: String) = throw UnsupportedOperationException() + override fun getFolderServerIds(): List = emptyList() + override fun createFolderUpdater(): BackendFolderUpdater = object : BackendFolderUpdater { + override fun createFolders(folders: List): Set = emptySet() + override fun deleteFolders(folderServerIds: List) = Unit + override fun changeFolder( + folderServerId: String, + name: String, + type: FolderType, + ) = Unit + override fun close() = Unit + } + override fun getExtraString(name: String): String? = null + override fun setExtraString(name: String, value: String) = Unit + override fun getExtraNumber(name: String): Long? = null + override fun setExtraNumber(name: String, value: Long) = Unit + } + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDtoSpecialFolderUpdaterFactory.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDtoSpecialFolderUpdaterFactory.kt new file mode 100644 index 00000000000..218a916a548 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyAccountDtoSpecialFolderUpdaterFactory.kt @@ -0,0 +1,24 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.mailstore.LegacyAccountDtoSpecialFolderUpdaterFactory +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.feature.mail.folder.api.FolderType +import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection +import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater + +internal class FakeLegacyAccountDtoSpecialFolderUpdaterFactory : LegacyAccountDtoSpecialFolderUpdaterFactory { + var lastAccount: LegacyAccountDto? = null + + override fun create(account: LegacyAccountDto): SpecialFolderUpdater { + lastAccount = account + + return object : SpecialFolderUpdater { + override fun updateSpecialFolders() = Unit + override fun setSpecialFolder( + type: FolderType, + folderId: Long?, + selection: SpecialFolderSelection, + ) = Unit + } + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyBackendFactory.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyBackendFactory.kt new file mode 100644 index 00000000000..72514ad6414 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/FakeLegacyBackendFactory.kt @@ -0,0 +1,95 @@ +package net.thunderbird.app.common.feature.mail + +import com.fsck.k9.backend.api.Backend +import com.fsck.k9.backend.api.BackendPusher +import com.fsck.k9.backend.api.BackendPusherCallback +import com.fsck.k9.backend.api.SyncConfig +import com.fsck.k9.backend.api.SyncListener +import com.fsck.k9.backends.ImapBackendFactory +import com.fsck.k9.mail.BodyFactory +import com.fsck.k9.mail.Flag +import com.fsck.k9.mail.Message +import com.fsck.k9.mail.Part +import net.thunderbird.core.android.account.LegacyAccountDto + +internal class FakeLegacyBackendFactory : ImapBackendFactory { + var lastAccount: LegacyAccountDto? = null + + override fun createBackend(account: LegacyAccountDto): Backend { + lastAccount = account + + return object : Backend { + override val supportsFlags = false + override val supportsExpunge = false + override val supportsMove = false + override val supportsCopy = false + override val supportsUpload = false + override val supportsTrashFolder = false + override val supportsSearchByDate = false + override val supportsFolderSubscriptions = false + override val isPushCapable = false + override fun refreshFolderList() = null + override fun sync( + folderServerId: String, + syncConfig: SyncConfig, + listener: SyncListener, + ) = Unit + override fun downloadMessage( + syncConfig: SyncConfig, + folderServerId: String, + messageServerId: String, + ) = Unit + override fun downloadMessageStructure(folderServerId: String, messageServerId: String) = Unit + override fun downloadCompleteMessage(folderServerId: String, messageServerId: String) = Unit + override fun setFlag( + folderServerId: String, + messageServerIds: List, + flag: Flag, + newState: Boolean, + ) = Unit + override fun markAllAsRead(folderServerId: String) = Unit + override fun expunge(folderServerId: String) = Unit + override fun deleteMessages(folderServerId: String, messageServerIds: List) = Unit + override fun deleteAllMessages(folderServerId: String) = Unit + override fun moveMessages( + sourceFolderServerId: String, + targetFolderServerId: String, + messageServerIds: List, + ) = null + override fun moveMessagesAndMarkAsRead( + sourceFolderServerId: String, + targetFolderServerId: String, + messageServerIds: List, + ) = null + override fun copyMessages( + sourceFolderServerId: String, + targetFolderServerId: String, + messageServerIds: List, + ) = null + override fun search( + folderServerId: String, + query: String?, + requiredFlags: Set?, + forbiddenFlags: Set?, + performFullTextSearch: Boolean, + ) = emptyList() + override fun fetchPart( + folderServerId: String, + messageServerId: String, + part: Part, + bodyFactory: BodyFactory, + ) = Unit + override fun findByMessageId(folderServerId: String, messageId: String) = null + override fun uploadMessage(folderServerId: String, message: Message) = null + override fun sendMessage(message: Message) = Unit + override fun createPusher( + callback: BackendPusherCallback, + ) = object : BackendPusher { + override fun start() = Unit + override fun updateFolders(folderServerIds: Collection) = Unit + override fun stop() = Unit + override fun reconnect() = Unit + } + } + } +} diff --git a/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/RemoteFolderCreatorResolverTest.kt b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/RemoteFolderCreatorResolverTest.kt new file mode 100644 index 00000000000..a64da9893a9 --- /dev/null +++ b/app-common/src/test/kotlin/net/thunderbird/app/common/feature/mail/RemoteFolderCreatorResolverTest.kt @@ -0,0 +1,44 @@ +package net.thunderbird.app.common.feature.mail + +import assertk.assertThat +import assertk.assertions.isNotSameInstanceAs +import assertk.assertions.isSameInstanceAs +import kotlin.test.Test +import net.thunderbird.core.common.mail.Protocols + +class RemoteFolderCreatorResolverTest { + + private val imapFactory = FakeImapRemoteFolderCreatorFactory() + + private val router = RemoteFolderCreatorResolver(imapFactory = imapFactory) + + @Test + fun `when account is LegacyAccountDto and IMAP then delegate to imapFactory`() { + // Arrange + val account = FakeData.legacyAccountDto.apply { + incomingServerSettings = incomingServerSettings.copy(type = Protocols.IMAP) + outgoingServerSettings = outgoingServerSettings.copy(type = Protocols.IMAP) + } + + // Act + val result = router.create(account) + + // Assert + assertThat(result).isNotSameInstanceAs(NoOpRemoteFolderCreator) + } + + @Test + fun `when account is LegacyAccountDto and POP3 then return NoOp`() { + // Arrange + val account = FakeData.legacyAccountDto.apply { + incomingServerSettings = incomingServerSettings.copy(type = Protocols.POP3) + outgoingServerSettings = outgoingServerSettings.copy(type = Protocols.POP3) + } + + // Act + val result = router.create(account) + + // Assert + assertThat(result).isSameInstanceAs(NoOpRemoteFolderCreator) + } +} diff --git a/app-k9mail/src/debug/kotlin/app/k9mail/dev/DemoBackendFactory.kt b/app-k9mail/src/debug/kotlin/app/k9mail/dev/DemoBackendFactory.kt index 720b79b3826..7c3865251f8 100644 --- a/app-k9mail/src/debug/kotlin/app/k9mail/dev/DemoBackendFactory.kt +++ b/app-k9mail/src/debug/kotlin/app/k9mail/dev/DemoBackendFactory.kt @@ -3,10 +3,10 @@ package app.k9mail.dev import app.k9mail.backend.demo.DemoBackend import com.fsck.k9.backend.BackendFactory import com.fsck.k9.backend.api.Backend -import com.fsck.k9.mailstore.K9BackendStorageFactory +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory import net.thunderbird.core.android.account.LegacyAccountDto -class DemoBackendFactory(private val backendStorageFactory: K9BackendStorageFactory) : BackendFactory { +class DemoBackendFactory(private val backendStorageFactory: LegacyAccountDtoBackendStorageFactory) : BackendFactory { override fun createBackend(account: LegacyAccountDto): Backend { val backendStorage = backendStorageFactory.createBackendStorage(account) return DemoBackend(backendStorage) diff --git a/app-thunderbird/src/daily/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt b/app-thunderbird/src/daily/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt index 2180b35ddc5..721aa5f75be 100644 --- a/app-thunderbird/src/daily/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt +++ b/app-thunderbird/src/daily/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt @@ -3,10 +3,10 @@ package net.thunderbird.android.dev import app.k9mail.backend.demo.DemoBackend import com.fsck.k9.backend.BackendFactory import com.fsck.k9.backend.api.Backend -import com.fsck.k9.mailstore.K9BackendStorageFactory +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory import net.thunderbird.core.android.account.LegacyAccountDto -class DemoBackendFactory(private val backendStorageFactory: K9BackendStorageFactory) : BackendFactory { +class DemoBackendFactory(private val backendStorageFactory: LegacyAccountDtoBackendStorageFactory) : BackendFactory { override fun createBackend(account: LegacyAccountDto): Backend { val backendStorage = backendStorageFactory.createBackendStorage(account) return DemoBackend(backendStorage) diff --git a/app-thunderbird/src/debug/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt b/app-thunderbird/src/debug/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt index 2180b35ddc5..721aa5f75be 100644 --- a/app-thunderbird/src/debug/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt +++ b/app-thunderbird/src/debug/kotlin/net/thunderbird/android/dev/DemoBackendFactory.kt @@ -3,10 +3,10 @@ package net.thunderbird.android.dev import app.k9mail.backend.demo.DemoBackend import com.fsck.k9.backend.BackendFactory import com.fsck.k9.backend.api.Backend -import com.fsck.k9.mailstore.K9BackendStorageFactory +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory import net.thunderbird.core.android.account.LegacyAccountDto -class DemoBackendFactory(private val backendStorageFactory: K9BackendStorageFactory) : BackendFactory { +class DemoBackendFactory(private val backendStorageFactory: LegacyAccountDtoBackendStorageFactory) : BackendFactory { override fun createBackend(account: LegacyAccountDto): Backend { val backendStorage = backendStorageFactory.createBackendStorage(account) return DemoBackend(backendStorage) diff --git a/backend/imap/src/main/kotlin/net/thunderbird/backend/imap/ImapRemoteFolderCreator.kt b/backend/imap/src/main/kotlin/net/thunderbird/backend/imap/ImapRemoteFolderCreator.kt index ba7b0734614..53494da8d0e 100644 --- a/backend/imap/src/main/kotlin/net/thunderbird/backend/imap/ImapRemoteFolderCreator.kt +++ b/backend/imap/src/main/kotlin/net/thunderbird/backend/imap/ImapRemoteFolderCreator.kt @@ -60,10 +60,12 @@ class ImapRemoteFolderCreator( } } -class ImapRemoteFolderCreatorFactory( +interface ImapRemoteFolderCreatorFactory : RemoteFolderCreator.Factory + +class DefaultImapRemoteFolderCreatorFactory( private val logger: Logger, private val backendFactory: BackendFactory, -) : RemoteFolderCreator.Factory { +) : ImapRemoteFolderCreatorFactory { override fun create(account: BaseAccount): RemoteFolderCreator { val backend = backendFactory.createBackend(account) as ImapBackend return ImapRemoteFolderCreator( diff --git a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountRemovedListener.kt b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountRemovedListener.kt index 52698f171a9..3b0dfeeb45f 100644 --- a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountRemovedListener.kt +++ b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountRemovedListener.kt @@ -1,5 +1,7 @@ package net.thunderbird.core.android.account +import net.thunderbird.feature.account.AccountId + fun interface AccountRemovedListener { - fun onAccountRemoved(account: LegacyAccountDto) + fun onAccountRemoved(id: AccountId) } diff --git a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDto.kt b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDto.kt index fd81ab2f3dd..a7edf3b4ebc 100644 --- a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDto.kt +++ b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDto.kt @@ -21,7 +21,7 @@ const val DEFAULT_VISIBLE_LIMIT = 25 /** * Account stores all of the settings for a single account defined by the user. Each account is defined by a UUID. */ -@Deprecated("Use LegacyAccount instead") +@Deprecated(message = "Use LegacyAccount instead, this class is only for compatibility with existing code.") @Suppress("TooManyFunctions") open class LegacyAccountDto( override val uuid: String, diff --git a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountManager.kt b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDtoManager.kt similarity index 70% rename from core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountManager.kt rename to core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDtoManager.kt index 396661cdff0..cb6187d82cc 100644 --- a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountManager.kt +++ b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountDtoManager.kt @@ -4,14 +4,13 @@ import kotlinx.coroutines.flow.Flow import net.thunderbird.feature.mail.account.api.AccountManager @Deprecated( - message = "Use net.thunderbird.feature.mail.account.api.AccountManager instead", + message = "Use net.thunderbird.core.android.account.LegacyAccountManager instead", replaceWith = ReplaceWith( - expression = "AccountManager", - "net.thunderbird.feature.mail.account.api.AccountManager", - "app.k9mail.legacy.account.LegacyAccount", + expression = "LegacyAccountManager", + "net.thunderbird.core.android.account.LegacyAccountManager", ), ) -interface AccountManager : AccountManager { +interface LegacyAccountDtoManager : AccountManager { override fun getAccounts(): List override fun getAccountsFlow(): Flow> override fun getAccount(accountUuid: String): LegacyAccountDto? diff --git a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountManager.kt b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountManager.kt index 949e5b8ec75..9982fa8155f 100644 --- a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountManager.kt +++ b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/LegacyAccountManager.kt @@ -2,8 +2,9 @@ package net.thunderbird.core.android.account import kotlinx.coroutines.flow.Flow import net.thunderbird.feature.account.AccountId +import net.thunderbird.feature.mail.account.api.AccountManager -interface LegacyAccountManager { +interface LegacyAccountManager : AccountManager { fun getAll(): Flow> fun getById(id: AccountId): Flow diff --git a/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/AccountStorageLegacyModule.kt b/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/AccountStorageLegacyModule.kt index 5a1ed0bc72d..31c57d78dfb 100644 --- a/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/AccountStorageLegacyModule.kt +++ b/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/AccountStorageLegacyModule.kt @@ -2,15 +2,16 @@ package net.thunderbird.feature.account.storage.legacy import net.thunderbird.feature.account.storage.legacy.mapper.DefaultAccountAvatarDataMapper import net.thunderbird.feature.account.storage.legacy.mapper.DefaultAccountProfileDataMapper -import net.thunderbird.feature.account.storage.legacy.mapper.DefaultLegacyAccountWrapperDataMapper +import net.thunderbird.feature.account.storage.legacy.mapper.DefaultLegacyAccountDataMapper +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper import net.thunderbird.feature.account.storage.legacy.serializer.ServerSettingsDtoSerializer import net.thunderbird.feature.account.storage.mapper.AccountAvatarDataMapper import net.thunderbird.feature.account.storage.mapper.AccountProfileDataMapper import org.koin.dsl.module val featureAccountStorageLegacyModule = module { - factory { - DefaultLegacyAccountWrapperDataMapper() + factory { + DefaultLegacyAccountDataMapper() } factory { diff --git a/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapper.kt b/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountDataMapper.kt similarity index 98% rename from feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapper.kt rename to feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountDataMapper.kt index 2b303c3e36f..b646387e2a5 100644 --- a/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapper.kt +++ b/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountDataMapper.kt @@ -2,10 +2,9 @@ package net.thunderbird.feature.account.storage.legacy.mapper import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.account.LegacyAccountDto -import net.thunderbird.core.architecture.data.DataMapper import net.thunderbird.feature.account.storage.profile.ProfileDto -class DefaultLegacyAccountWrapperDataMapper : DataMapper { +internal class DefaultLegacyAccountDataMapper : LegacyAccountDataMapper { @Suppress("LongMethod") override fun toDomain(dto: LegacyAccountDto): LegacyAccount { diff --git a/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/LegacyAccountDataMapper.kt b/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/LegacyAccountDataMapper.kt new file mode 100644 index 00000000000..ce851a75f83 --- /dev/null +++ b/feature/account/storage/legacy/src/main/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/LegacyAccountDataMapper.kt @@ -0,0 +1,10 @@ +package net.thunderbird.feature.account.storage.legacy.mapper + +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.architecture.data.DataMapper + +/** + * Mapper for converting between [LegacyAccount] and [LegacyAccountDto]. + */ +interface LegacyAccountDataMapper : DataMapper diff --git a/feature/account/storage/legacy/src/test/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapperTest.kt b/feature/account/storage/legacy/src/test/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapperTest.kt index 7d15eda5175..72a85fd5625 100644 --- a/feature/account/storage/legacy/src/test/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapperTest.kt +++ b/feature/account/storage/legacy/src/test/kotlin/net/thunderbird/feature/account/storage/legacy/mapper/DefaultLegacyAccountWrapperDataMapperTest.kt @@ -31,7 +31,7 @@ class DefaultLegacyAccountWrapperDataMapperTest { // arrange val account = createAccount() val expected = createAccountWrapper() - val testSubject = DefaultLegacyAccountWrapperDataMapper() + val testSubject = DefaultLegacyAccountDataMapper() // act val result = testSubject.toDomain(account) @@ -45,7 +45,7 @@ class DefaultLegacyAccountWrapperDataMapperTest { fun `toDto should return account`() { // arrange val wrapper = createAccountWrapper() - val testSubject = DefaultLegacyAccountWrapperDataMapper() + val testSubject = DefaultLegacyAccountDataMapper() // act val result = testSubject.toDto(wrapper) diff --git a/feature/mail/message/list/src/main/kotlin/net/thunderbird/feature/mail/message/list/FeatureMessageListModule.kt b/feature/mail/message/list/src/main/kotlin/net/thunderbird/feature/mail/message/list/FeatureMessageListModule.kt index f152ccff2e2..c9b205d1409 100644 --- a/feature/mail/message/list/src/main/kotlin/net/thunderbird/feature/mail/message/list/FeatureMessageListModule.kt +++ b/feature/mail/message/list/src/main/kotlin/net/thunderbird/feature/mail/message/list/FeatureMessageListModule.kt @@ -11,7 +11,6 @@ import net.thunderbird.feature.mail.message.list.ui.dialog.SetupArchiveFolderDia import net.thunderbird.feature.mail.message.list.ui.dialog.SetupArchiveFolderDialogFragmentFactory import net.thunderbird.feature.mail.message.list.ui.dialog.SetupArchiveFolderDialogViewModel import org.koin.core.module.dsl.viewModel -import org.koin.core.qualifier.named import org.koin.dsl.module val featureMessageListModule = module { @@ -21,7 +20,7 @@ val featureMessageListModule = module { accountManager = get(), backendStorageFactory = get(), specialFolderUpdaterFactory = get(), - remoteFolderCreatorFactory = get(named("imap")), + remoteFolderCreatorFactory = get(), ) } factory { diff --git a/feature/migration/provider/src/main/kotlin/app/k9mail/feature/migration/provider/SettingsProvider.kt b/feature/migration/provider/src/main/kotlin/app/k9mail/feature/migration/provider/SettingsProvider.kt index 6f07137c82b..c41bd68c6f1 100644 --- a/feature/migration/provider/src/main/kotlin/app/k9mail/feature/migration/provider/SettingsProvider.kt +++ b/feature/migration/provider/src/main/kotlin/app/k9mail/feature/migration/provider/SettingsProvider.kt @@ -13,7 +13,7 @@ import com.fsck.k9.helper.MimeTypeUtil import com.fsck.k9.helper.mapToSet import com.fsck.k9.preferences.SettingsExporter import kotlin.concurrent.thread -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log import okio.ByteString.Companion.toByteString import org.koin.android.ext.android.inject @@ -27,7 +27,7 @@ import org.koin.core.component.KoinComponent * settings (including passwords). */ class SettingsProvider : ContentProvider(), KoinComponent { - private val accountManager: AccountManager by inject() + private val accountManager: LegacyAccountDtoManager by inject() private val settingsExporter: SettingsExporter by inject() override fun onCreate(): Boolean { diff --git a/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/GetDisplayAccounts.kt b/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/GetDisplayAccounts.kt index 5f681c5fe17..610946001e7 100644 --- a/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/GetDisplayAccounts.kt +++ b/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/GetDisplayAccounts.kt @@ -14,15 +14,15 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount internal class GetDisplayAccounts( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messageCountsProvider: MessageCountsProvider, private val messageListRepository: MessageListRepository, private val coroutineContext: CoroutineContext = Dispatchers.IO, diff --git a/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccount.kt b/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccount.kt index 0cf84a222b6..d69a5d1bf20 100644 --- a/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccount.kt +++ b/feature/navigation/drawer/dropdown/src/main/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccount.kt @@ -9,12 +9,12 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase internal class SyncAccount( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messagingController: MessagingControllerMailChecker, private val coroutineContext: CoroutineContext = Dispatchers.IO, ) : UseCase.SyncAccount { diff --git a/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/FakeAccountManager.kt b/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/FakeLegacyAccountDtoManager.kt similarity index 91% rename from feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/FakeAccountManager.kt rename to feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/FakeLegacyAccountDtoManager.kt index d818976fa56..3e05680e32e 100644 --- a/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/FakeAccountManager.kt +++ b/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/FakeLegacyAccountDtoManager.kt @@ -1,15 +1,15 @@ package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase import kotlinx.coroutines.flow.Flow -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.AccountRemovedListener import net.thunderbird.core.android.account.AccountsChangeListener import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager -internal class FakeAccountManager( +internal class FakeLegacyAccountDtoManager( val recordedParameters: MutableList = mutableListOf(), private val accounts: List = emptyList(), -) : AccountManager { +) : LegacyAccountDtoManager { override fun getAccounts(): List { TODO("Not yet implemented") } diff --git a/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccountTest.kt b/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccountTest.kt index 4b9c088ced4..47a0032df10 100644 --- a/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccountTest.kt +++ b/feature/navigation/drawer/dropdown/src/test/kotlin/net/thunderbird/feature/navigation/drawer/dropdown/domain/usecase/SyncAccountTest.kt @@ -16,7 +16,7 @@ internal class SyncAccountTest { listener?.checkMailFinished(null, null) } val account = FakeData.ACCOUNT - val accountManager = FakeAccountManager( + val accountManager = FakeLegacyAccountDtoManager( accounts = listOf(account), ) val messagingController = FakeMessagingControllerMailChecker( diff --git a/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/GetDisplayAccounts.kt b/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/GetDisplayAccounts.kt index efcd7193d6b..d70ddae2c6b 100644 --- a/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/GetDisplayAccounts.kt +++ b/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/GetDisplayAccounts.kt @@ -14,13 +14,13 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.navigation.drawer.siderail.domain.DomainContract import net.thunderbird.feature.navigation.drawer.siderail.domain.entity.DisplayAccount internal class GetDisplayAccounts( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messageCountsProvider: MessageCountsProvider, private val messageListRepository: MessageListRepository, private val coroutineContext: CoroutineContext = Dispatchers.IO, diff --git a/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccount.kt b/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccount.kt index d19c6ac02e3..5884c8d9836 100644 --- a/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccount.kt +++ b/feature/navigation/drawer/siderail/src/main/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccount.kt @@ -9,12 +9,12 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.navigation.drawer.siderail.domain.DomainContract internal class SyncAccount( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messagingController: MessagingControllerMailChecker, private val coroutineContext: CoroutineContext = Dispatchers.IO, ) : DomainContract.UseCase.SyncAccount { diff --git a/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/FakeAccountManager.kt b/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/FakeLegacyAccountDtoManager.kt similarity index 91% rename from feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/FakeAccountManager.kt rename to feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/FakeLegacyAccountDtoManager.kt index 5d1b7a03a7f..9431c8f6819 100644 --- a/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/FakeAccountManager.kt +++ b/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/FakeLegacyAccountDtoManager.kt @@ -1,15 +1,15 @@ package net.thunderbird.feature.navigation.drawer.siderail.domain.usecase import kotlinx.coroutines.flow.Flow -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.AccountRemovedListener import net.thunderbird.core.android.account.AccountsChangeListener import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager -internal class FakeAccountManager( +internal class FakeLegacyAccountDtoManager( val recordedParameters: MutableList = mutableListOf(), private val accounts: List = emptyList(), -) : AccountManager { +) : LegacyAccountDtoManager { override fun getAccounts(): List { TODO("Not yet implemented") } diff --git a/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccountTest.kt b/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccountTest.kt index 39ad990acad..9761b8b606f 100644 --- a/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccountTest.kt +++ b/feature/navigation/drawer/siderail/src/test/kotlin/net/thunderbird/feature/navigation/drawer/siderail/domain/usecase/SyncAccountTest.kt @@ -16,7 +16,7 @@ internal class SyncAccountTest { listener?.checkMailFinished(null, null) } val account = FakeData.ACCOUNT - val accountManager = FakeAccountManager( + val accountManager = FakeLegacyAccountDtoManager( accounts = listOf(account), ) val messagingController = FakeMessagingControllerMailChecker( diff --git a/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/AuthViewModel.kt b/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/AuthViewModel.kt index e229b7798c7..dbdfafba010 100644 --- a/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/AuthViewModel.kt +++ b/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/AuthViewModel.kt @@ -26,8 +26,8 @@ import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationResponse import net.openid.appauth.AuthorizationService -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log private const val KEY_AUTHORIZATION = "app.k9mail_auth" @@ -35,7 +35,7 @@ private const val KEY_AUTHORIZATION = "app.k9mail_auth" @Suppress("TooManyFunctions") internal class AuthViewModel( application: Application, - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val getOAuthRequestIntent: GetOAuthRequestIntent, ) : AndroidViewModel(application) { private var authService: AuthorizationService? = null diff --git a/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/OAuthFlowActivity.kt b/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/OAuthFlowActivity.kt index ea1b072af89..b66f7002a54 100644 --- a/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/OAuthFlowActivity.kt +++ b/feature/settings/import/src/main/kotlin/app/k9mail/feature/settings/import/ui/OAuthFlowActivity.kt @@ -16,14 +16,14 @@ import com.google.android.material.textview.MaterialTextView import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.launch -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel @Deprecated("Remove once import used the new oauth flow") class OAuthFlowActivity : K9Activity() { private val authViewModel: AuthViewModel by viewModel() - private val accountManager: AccountManager by inject() + private val accountManager: LegacyAccountDtoManager by inject() private lateinit var errorText: MaterialTextView private lateinit var signInButton: Button diff --git a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/KoinModule.kt b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/KoinModule.kt index f7a061c8066..cb1ace0c267 100644 --- a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/KoinModule.kt +++ b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/KoinModule.kt @@ -5,7 +5,7 @@ import org.koin.dsl.module val featureWidgetMessageListModule = module { factory { MessageListLoader( - preferences = get(), + accountManager = get(), messageListRepository = get(), messageHelper = get(), generalSettingsManager = get(), diff --git a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt index c36f7abe42f..1c9c9bd02cc 100644 --- a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt +++ b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt @@ -7,12 +7,12 @@ import com.fsck.k9.helper.MessageHelper import com.fsck.k9.ui.helper.DisplayAddressHelper import java.util.Calendar import java.util.Locale -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.preference.GeneralSettingsManager internal class MessageListItemMapper( private val messageHelper: MessageHelper, - private val account: LegacyAccountDto, + private val account: LegacyAccount, private val generalSettingsManager: GeneralSettingsManager, ) : MessageMapper { private val calendar: Calendar = Calendar.getInstance() @@ -51,7 +51,7 @@ internal class MessageListItemMapper( isRead = message.isRead, hasAttachments = message.hasAttachments, threadCount = message.threadCount, - accountColor = account.chipColor, + accountColor = account.profile.color, messageReference = MessageReference(account.uuid, message.folderId, message.messageServerId), uniqueId = uniqueId, sortSubject = message.subject, @@ -71,7 +71,7 @@ internal class MessageListItemMapper( return String.format("%d %s", dayOfMonth, month) } - private fun createUniqueId(account: LegacyAccountDto, messageId: Long): Long { + private fun createUniqueId(account: LegacyAccount, messageId: Long): Long { return ((account.accountNumber + 1).toLong() shl ACCOUNT_NUMBER_BIT_SHIFT) + messageId } diff --git a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt index e58414f22c3..31b39e65525 100644 --- a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt +++ b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt @@ -1,18 +1,18 @@ package net.thunderbird.feature.widget.message.list import app.k9mail.legacy.mailstore.MessageListRepository -import com.fsck.k9.Preferences import com.fsck.k9.helper.MessageHelper import com.fsck.k9.mailstore.MessageColumns -import com.fsck.k9.search.getAccounts -import net.thunderbird.core.android.account.LegacyAccountDto +import com.fsck.k9.search.getLegacyAccounts +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.core.android.account.SortType import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.feature.search.legacy.sql.SqlWhereClause internal class MessageListLoader( - private val preferences: Preferences, + private val accountManager: LegacyAccountManager, private val messageListRepository: MessageListRepository, private val messageHelper: MessageHelper, private val generalSettingsManager: GeneralSettingsManager, @@ -31,7 +31,7 @@ internal class MessageListLoader( } private fun getMessageListInfo(config: MessageListConfig): List { - val accounts = config.search.getAccounts(preferences) + val accounts = config.search.getLegacyAccounts(accountManager) val messageListItems = accounts .flatMap { account -> loadMessageListForAccount(account, config) @@ -41,7 +41,7 @@ internal class MessageListLoader( return messageListItems } - private fun loadMessageListForAccount(account: LegacyAccountDto, config: MessageListConfig): List { + private fun loadMessageListForAccount(account: LegacyAccount, config: MessageListConfig): List { val accountUuid = account.uuid val sortOrder = buildSortOrder(config) val mapper = MessageListItemMapper(messageHelper, account, generalSettingsManager) diff --git a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/KoinModule.kt b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/KoinModule.kt index bebab0517fb..5bf7991903a 100644 --- a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/KoinModule.kt +++ b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/KoinModule.kt @@ -6,7 +6,7 @@ val messageListWidgetModule = module { single { MessageListWidgetManager(context = get(), messageListRepository = get(), config = get()) } factory { MessageListLoader( - preferences = get(), + accountManager = get(), messageListRepository = get(), messageHelper = get(), generalSettingsManager = get(), diff --git a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt index f9c6769d73f..ea2654b3f02 100644 --- a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt +++ b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt @@ -7,12 +7,12 @@ import com.fsck.k9.helper.MessageHelper import com.fsck.k9.ui.helper.DisplayAddressHelper import java.util.Calendar import java.util.Locale -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.preference.GeneralSettingsManager internal class MessageListItemMapper( private val messageHelper: MessageHelper, - private val account: LegacyAccountDto, + private val account: LegacyAccount, private val generalSettingsManager: GeneralSettingsManager, ) : MessageMapper { private val calendar: Calendar = Calendar.getInstance() @@ -45,7 +45,7 @@ internal class MessageListItemMapper( isRead = message.isRead, hasAttachments = message.hasAttachments, threadCount = message.threadCount, - accountColor = account.chipColor, + accountColor = account.profile.color, messageReference = MessageReference(account.uuid, message.folderId, message.messageServerId), uniqueId = uniqueId, sortSubject = message.subject, @@ -65,7 +65,7 @@ internal class MessageListItemMapper( return String.format("%d %s", dayOfMonth, month) } - private fun createUniqueId(account: LegacyAccountDto, messageId: Long): Long { + private fun createUniqueId(account: LegacyAccount, messageId: Long): Long { return ((account.accountNumber + 1).toLong() shl ACCOUNT_NUMBER_BIT_SHIFT) + messageId } diff --git a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt index 9ac651b4bff..334af96b5d8 100644 --- a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt +++ b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt @@ -1,18 +1,18 @@ package app.k9mail.feature.widget.message.list import app.k9mail.legacy.mailstore.MessageListRepository -import com.fsck.k9.Preferences import com.fsck.k9.helper.MessageHelper import com.fsck.k9.mailstore.MessageColumns -import com.fsck.k9.search.getAccounts -import net.thunderbird.core.android.account.LegacyAccountDto +import com.fsck.k9.search.getLegacyAccounts +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.core.android.account.SortType import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.feature.search.legacy.sql.SqlWhereClause internal class MessageListLoader( - private val preferences: Preferences, + private val accountManager: LegacyAccountManager, private val messageListRepository: MessageListRepository, private val messageHelper: MessageHelper, private val generalSettingsManager: GeneralSettingsManager, @@ -31,7 +31,7 @@ internal class MessageListLoader( } private fun getMessageListInfo(config: MessageListConfig): List { - val accounts = config.search.getAccounts(preferences) + val accounts = config.search.getLegacyAccounts(accountManager) val messageListItems = accounts .flatMap { account -> loadMessageListForAccount(account, config) @@ -41,7 +41,7 @@ internal class MessageListLoader( return messageListItems } - private fun loadMessageListForAccount(account: LegacyAccountDto, config: MessageListConfig): List { + private fun loadMessageListForAccount(account: LegacyAccount, config: MessageListConfig): List { val accountUuid = account.uuid val sortOrder = buildSortOrder(config) val mapper = MessageListItemMapper(messageHelper, account, generalSettingsManager) diff --git a/legacy/common/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt b/legacy/common/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt index e9bc4f6fa96..5e806e39b92 100644 --- a/legacy/common/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt +++ b/legacy/common/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt @@ -13,12 +13,12 @@ import com.fsck.k9.mail.store.imap.ImapStoreSettings.pathPrefix import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.common.mail.Protocols import net.thunderbird.core.logging.legacy.Log class AccountServerSettingsUpdater( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : AccountEditExternalContract.AccountServerSettingsUpdater { diff --git a/legacy/common/src/main/java/com/fsck/k9/account/AccountStateLoader.kt b/legacy/common/src/main/java/com/fsck/k9/account/AccountStateLoader.kt index afc0cf0411d..af51fb07249 100644 --- a/legacy/common/src/main/java/com/fsck/k9/account/AccountStateLoader.kt +++ b/legacy/common/src/main/java/com/fsck/k9/account/AccountStateLoader.kt @@ -8,13 +8,13 @@ import com.fsck.k9.mail.ServerSettings import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.common.mail.Protocols import net.thunderbird.core.logging.legacy.Log class AccountStateLoader( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : AccountCommonExternalContract.AccountStateLoader { diff --git a/legacy/common/src/main/java/com/fsck/k9/backends/AccountAuthStateStorage.kt b/legacy/common/src/main/java/com/fsck/k9/backends/AccountAuthStateStorage.kt index 6b863ed962e..e3f5d05fa3d 100644 --- a/legacy/common/src/main/java/com/fsck/k9/backends/AccountAuthStateStorage.kt +++ b/legacy/common/src/main/java/com/fsck/k9/backends/AccountAuthStateStorage.kt @@ -1,11 +1,11 @@ package com.fsck.k9.backends import com.fsck.k9.mail.oauth.AuthStateStorage -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager class AccountAuthStateStorage( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val account: LegacyAccountDto, ) : AuthStateStorage { override fun getAuthorizationState(): String? { diff --git a/legacy/common/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt b/legacy/common/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt index 987b0e2c247..5c98ec56e4f 100644 --- a/legacy/common/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt +++ b/legacy/common/src/main/java/com/fsck/k9/backends/ImapBackendFactory.kt @@ -13,26 +13,28 @@ import com.fsck.k9.mail.store.imap.ImapClientInfo import com.fsck.k9.mail.store.imap.ImapStore import com.fsck.k9.mail.store.imap.ImapStoreConfig import com.fsck.k9.mail.transport.smtp.SmtpTransport -import com.fsck.k9.mailstore.K9BackendStorageFactory +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.Expunge import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager + +interface ImapBackendFactory : BackendFactory @Suppress("LongParameterList") -class ImapBackendFactory( - private val accountManager: AccountManager, +class DefaultImapBackendFactory( + private val accountManager: LegacyAccountDtoManager, private val powerManager: PowerManager, private val idleRefreshManager: IdleRefreshManager, - private val backendStorageFactory: K9BackendStorageFactory, + private val backendStorageFactory: LegacyAccountDtoBackendStorageFactory, private val trustedSocketFactory: TrustedSocketFactory, private val context: Context, private val clientInfoAppName: String, private val clientInfoAppVersion: String, -) : BackendFactory { +) : ImapBackendFactory { override fun createBackend(account: LegacyAccountDto): Backend { val accountName = account.displayName val backendStorage = backendStorageFactory.createBackendStorage(account) diff --git a/legacy/common/src/main/java/com/fsck/k9/backends/KoinModule.kt b/legacy/common/src/main/java/com/fsck/k9/backends/KoinModule.kt index ec2a0108a36..3b7e0a6be4f 100644 --- a/legacy/common/src/main/java/com/fsck/k9/backends/KoinModule.kt +++ b/legacy/common/src/main/java/com/fsck/k9/backends/KoinModule.kt @@ -5,9 +5,6 @@ import com.fsck.k9.backend.imap.BackendIdleRefreshManager import com.fsck.k9.backend.imap.SystemAlarmManager import com.fsck.k9.mail.oauth.OAuth2TokenProviderFactory import com.fsck.k9.mail.store.imap.IdleRefreshManager -import net.thunderbird.backend.api.BackendFactory -import net.thunderbird.backend.api.folder.RemoteFolderCreator -import net.thunderbird.backend.imap.ImapRemoteFolderCreatorFactory import org.koin.core.qualifier.named import org.koin.dsl.module import com.fsck.k9.backend.BackendFactory as LegacyBackendFactory @@ -22,8 +19,8 @@ val backendsModule = module { ) + developmentBackends, ) } - single { - ImapBackendFactory( + single { + DefaultImapBackendFactory( accountManager = get(), powerManager = get(), idleRefreshManager = get(), @@ -34,18 +31,6 @@ val backendsModule = module { clientInfoAppVersion = get(named("ClientInfoAppVersion")), ) } - - single>(named("imap")) { - get() - } - - single(named("imap")) { - ImapRemoteFolderCreatorFactory( - logger = get(), - backendFactory = get(named("imap")), - ) - } - single { AndroidAlarmManager(context = get(), alarmManager = get()) } single { BackendIdleRefreshManager(alarmManager = get()) } single { Pop3BackendFactory(get(), get()) } diff --git a/legacy/common/src/main/java/com/fsck/k9/backends/Pop3BackendFactory.kt b/legacy/common/src/main/java/com/fsck/k9/backends/Pop3BackendFactory.kt index 149abf51f39..2e176997daf 100644 --- a/legacy/common/src/main/java/com/fsck/k9/backends/Pop3BackendFactory.kt +++ b/legacy/common/src/main/java/com/fsck/k9/backends/Pop3BackendFactory.kt @@ -7,11 +7,11 @@ import com.fsck.k9.mail.oauth.OAuth2TokenProvider import com.fsck.k9.mail.ssl.TrustedSocketFactory import com.fsck.k9.mail.store.pop3.Pop3Store import com.fsck.k9.mail.transport.smtp.SmtpTransport -import com.fsck.k9.mailstore.K9BackendStorageFactory +import com.fsck.k9.mailstore.LegacyAccountDtoBackendStorageFactory import net.thunderbird.core.android.account.LegacyAccountDto class Pop3BackendFactory( - private val backendStorageFactory: K9BackendStorageFactory, + private val backendStorageFactory: LegacyAccountDtoBackendStorageFactory, private val trustedSocketFactory: TrustedSocketFactory, ) : BackendFactory { override fun createBackend(account: LegacyAccountDto): Backend { diff --git a/legacy/common/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt b/legacy/common/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt index edb46356089..06a4ce7cde8 100644 --- a/legacy/common/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt +++ b/legacy/common/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt @@ -29,7 +29,7 @@ class AccountServerSettingsUpdaterTest { @Test fun `updateServerSettings() SHOULD return account not found exception WHEN none present with uuid`() = runTest { - val accountManager = FakeAccountManager(accounts = mutableMapOf()) + val accountManager = FakeLegacyAccountDtoManager(accounts = mutableMapOf()) val testSubject = AccountServerSettingsUpdater(accountManager) val result = testSubject.updateServerSettings( @@ -48,7 +48,7 @@ class AccountServerSettingsUpdaterTest { @Test fun `updateServerSettings() SHOULD return success with updated incoming settings WHEN is incoming`() = runTest { - val accountManager = FakeAccountManager( + val accountManager = FakeLegacyAccountDtoManager( accounts = mutableMapOf(ACCOUNT_ID_RAW to createAccount(ACCOUNT_ID_RAW)), ) val updatedIncomingServerSettings = INCOMING_SERVER_SETTINGS.copy(port = 123) @@ -74,7 +74,7 @@ class AccountServerSettingsUpdaterTest { @Test fun `updateServerSettings() SHOULD return success with updated outgoing settings WHEN is not incoming`() = runTest { - val accountManager = FakeAccountManager( + val accountManager = FakeLegacyAccountDtoManager( accounts = mutableMapOf(ACCOUNT_ID_RAW to createAccount(ACCOUNT_ID_RAW)), ) val updatedOutgoingServerSettings = OUTGOING_SERVER_SETTINGS.copy(port = 123) @@ -100,7 +100,7 @@ class AccountServerSettingsUpdaterTest { @Test fun `updateServerSettings() SHOULD return unknown error when exception thrown`() = runTest { - val accountManager = FakeAccountManager( + val accountManager = FakeLegacyAccountDtoManager( accounts = mutableMapOf(ACCOUNT_ID_RAW to createAccount(ACCOUNT_ID_RAW)), isFailureOnSave = true, ) diff --git a/legacy/common/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt b/legacy/common/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt index 09090fcd7c2..d9d55767bd9 100644 --- a/legacy/common/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt +++ b/legacy/common/src/test/java/com/fsck/k9/account/AccountStateLoaderTest.kt @@ -18,7 +18,7 @@ class AccountStateLoaderTest { @Test fun `loadAccountState() SHOULD return null when accountManager returns null`() = runTest { - val accountManager = FakeAccountManager() + val accountManager = FakeLegacyAccountDtoManager() val testSubject = AccountStateLoader(accountManager) val result = testSubject.loadAccountState(ACCOUNT_ID_RAW) @@ -37,7 +37,7 @@ class AccountStateLoaderTest { oAuthState = "oAuthState" }, ) - val accountManager = FakeAccountManager(accounts = accounts) + val accountManager = FakeLegacyAccountDtoManager(accounts = accounts) val testSubject = AccountStateLoader(accountManager) val result = testSubject.loadAccountState(ACCOUNT_ID_RAW) diff --git a/legacy/common/src/test/java/com/fsck/k9/account/FakeAccountManager.kt b/legacy/common/src/test/java/com/fsck/k9/account/FakeLegacyAccountDtoManager.kt similarity index 92% rename from legacy/common/src/test/java/com/fsck/k9/account/FakeAccountManager.kt rename to legacy/common/src/test/java/com/fsck/k9/account/FakeLegacyAccountDtoManager.kt index 2c9a86cc2bc..7010f2047ca 100644 --- a/legacy/common/src/test/java/com/fsck/k9/account/FakeAccountManager.kt +++ b/legacy/common/src/test/java/com/fsck/k9/account/FakeLegacyAccountDtoManager.kt @@ -1,15 +1,15 @@ package com.fsck.k9.account import kotlinx.coroutines.flow.Flow -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.AccountRemovedListener import net.thunderbird.core.android.account.AccountsChangeListener import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager -class FakeAccountManager( +class FakeLegacyAccountDtoManager( private val accounts: MutableMap = mutableMapOf(), private val isFailureOnSave: Boolean = false, -) : AccountManager { +) : LegacyAccountDtoManager { override fun getAccounts(): List = accounts.values.toList() diff --git a/legacy/core/src/main/java/com/fsck/k9/Preferences.kt b/legacy/core/src/main/java/com/fsck/k9/Preferences.kt index 282e5bfe5d0..d0e2c7321ac 100644 --- a/legacy/core/src/main/java/com/fsck/k9/Preferences.kt +++ b/legacy/core/src/main/java/com/fsck/k9/Preferences.kt @@ -19,10 +19,10 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn import net.thunderbird.core.android.account.AccountDefaultsProvider import net.thunderbird.core.android.account.AccountDefaultsProvider.Companion.UNASSIGNED_ACCOUNT_NUMBER -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.AccountRemovedListener import net.thunderbird.core.android.account.AccountsChangeListener import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.common.exception.MessagingException import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager @@ -32,14 +32,14 @@ import net.thunderbird.core.preference.storage.StoragePersister import net.thunderbird.feature.account.storage.legacy.AccountDtoStorageHandler import org.koin.java.KoinJavaComponent.inject -@Suppress("MaxLineLength") +@Suppress("MaxLineLength", "TooManyFunctions") class Preferences internal constructor( private val storagePersister: StoragePersister, private val localStoreProvider: LocalStoreProvider, private val legacyAccountStorageHandler: AccountDtoStorageHandler, private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, private val accountDefaultsProvider: AccountDefaultsProvider, -) : AccountManager { +) : LegacyAccountDtoManager { private val generalSettingsManager: GeneralSettingsManager by inject(GeneralSettingsManager::class.java) private val accountLock = Any() @@ -316,7 +316,7 @@ class Preferences internal constructor( private fun notifyAccountRemovedListeners(account: LegacyAccountDto) { for (listener in accountRemovedListeners) { - listener.onAccountRemoved(account) + listener.onAccountRemoved(account.id) } } diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/DefaultMessageCountsProvider.kt b/legacy/core/src/main/java/com/fsck/k9/controller/DefaultMessageCountsProvider.kt index e8d5d100170..e786f4657de 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/DefaultMessageCountsProvider.kt +++ b/legacy/core/src/main/java/com/fsck/k9/controller/DefaultMessageCountsProvider.kt @@ -18,15 +18,15 @@ import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log import net.thunderbird.feature.search.legacy.LocalMessageSearch import net.thunderbird.feature.search.legacy.SearchAccount import net.thunderbird.feature.search.legacy.SearchConditionTreeNode internal class DefaultMessageCountsProvider( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messageStoreManager: MessageStoreManager, private val messagingControllerRegistry: MessagingControllerRegistry, private val coroutineContext: CoroutineContext = Dispatchers.IO, diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/KoinModule.kt b/legacy/core/src/main/java/com/fsck/k9/controller/KoinModule.kt index 05048efa803..34f6eb8bd2c 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/KoinModule.kt +++ b/legacy/core/src/main/java/com/fsck/k9/controller/KoinModule.kt @@ -15,6 +15,7 @@ import net.thunderbird.core.featureflag.FeatureFlagProvider import net.thunderbird.core.logging.Logger import net.thunderbird.feature.notification.api.sender.NotificationSender import org.koin.core.qualifier.named +import org.koin.dsl.binds import org.koin.dsl.module val controllerModule = module { @@ -35,6 +36,13 @@ val controllerModule = module { get(named("syncDebug")), get(), ) + } binds arrayOf(MessagingControllerRegistry::class) + + single { + MessagingControllerWrapper( + messagingController = get(), + accountManager = get(), + ) } single { get() } diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerWrapper.kt b/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerWrapper.kt new file mode 100644 index 00000000000..fb9f8606038 --- /dev/null +++ b/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerWrapper.kt @@ -0,0 +1,217 @@ +package com.fsck.k9.controller + +import app.k9mail.legacy.message.controller.MessageReference +import app.k9mail.legacy.message.controller.MessagingListener +import com.fsck.k9.mail.Flag +import java.util.concurrent.Future +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager +import net.thunderbird.feature.account.AccountId + +/** + * A wrapper around [MessagingController] that takes care of loading the account by [AccountId] and + * provides some convenience methods. + */ +@Suppress("TooManyFunctions") +class MessagingControllerWrapper( + private val messagingController: MessagingController, + private val accountManager: LegacyAccountDtoManager, +) { + + private fun getAccountDtoOrThrow(id: AccountId): LegacyAccountDto { + return accountManager.getAccount(id.asRaw()) ?: error("Account not found: $id") + } + + fun loadMoreMessages(id: AccountId, folderId: Long) { + val account = getAccountDtoOrThrow(id) + messagingController.loadMoreMessages(account, folderId) + } + + fun loadSearchResults( + id: AccountId, + folderId: Long, + messageServerIds: List, + listener: MessagingListener, + ) { + val account = getAccountDtoOrThrow(id) + messagingController.loadSearchResults(account, folderId, messageServerIds, listener) + } + + fun clearNewMessages(id: AccountId) { + val account = getAccountDtoOrThrow(id) + messagingController.clearNewMessages(account) + } + + fun searchRemoteMessages( + id: AccountId, + folderId: Long, + query: String?, + requiredFlags: Set?, + forbiddenFlags: Set?, + listener: MessagingListener, + ): Future<*>? = messagingController.searchRemoteMessages( + id.asRaw(), + folderId, + query, + requiredFlags, + forbiddenFlags, + listener, + ) + + fun expunge(id: AccountId, folderId: Long) { + val account = getAccountDtoOrThrow(id) + messagingController.expunge(account, folderId) + } + + fun sendPendingMessages(id: AccountId, listener: MessagingListener?) { + val account = getAccountDtoOrThrow(id) + messagingController.sendPendingMessages(account, listener) + } + + fun setFlagForThreads(id: AccountId, threadIds: List, flag: Flag, newState: Boolean) { + val account = getAccountDtoOrThrow(id) + messagingController.setFlagForThreads(account, threadIds, flag, newState) + } + + fun setFlag(id: AccountId, messageIds: List, flag: Flag, newState: Boolean) { + val account = getAccountDtoOrThrow(id) + messagingController.setFlag(account, messageIds, flag, newState) + } + + fun isMoveCapable(id: AccountId): Boolean { + val account = getAccountDtoOrThrow(id) + return messagingController.isMoveCapable(account) + } + + fun isCopyCapable(id: AccountId): Boolean { + val account = getAccountDtoOrThrow(id) + return messagingController.isCopyCapable(account) + } + + fun moveMessagesInThread( + id: AccountId, + folderId: Long, + messages: List, + destinationFolderId: Long, + ) { + val account = getAccountDtoOrThrow(id) + messagingController.moveMessagesInThread( + account, + folderId, + messages, + destinationFolderId, + ) + } + + fun moveMessages( + id: AccountId, + folderId: Long, + messages: List, + destinationFolderId: Long, + ) { + val account = getAccountDtoOrThrow(id) + messagingController.moveMessages( + account, + folderId, + messages, + destinationFolderId, + ) + } + + fun copyMessagesInThread( + id: AccountId, + folderId: Long, + messages: List, + destinationFolderId: Long, + ) { + val account = getAccountDtoOrThrow(id) + messagingController.copyMessagesInThread( + account, + folderId, + messages, + destinationFolderId, + ) + } + + fun copyMessages( + id: AccountId, + folderId: Long, + messages: List, + destinationFolderId: Long, + ) { + val account = getAccountDtoOrThrow(id) + messagingController.copyMessages( + account, + folderId, + messages, + destinationFolderId, + ) + } + + fun moveToDraftsFolder(id: AccountId, folderId: Long, messages: List) { + val account = getAccountDtoOrThrow(id) + messagingController.moveToDraftsFolder(account, folderId, messages) + } + + fun emptySpam(id: AccountId) { + val account = getAccountDtoOrThrow(id) + messagingController.emptySpam(account, null) + } + + fun emptyTrash(id: AccountId) { + val account = getAccountDtoOrThrow(id) + messagingController.emptyTrash(account, null) + } + + fun synchronizeMailbox(id: AccountId, folderId: Long, notify: Boolean, listener: MessagingListener?) { + val account = getAccountDtoOrThrow(id) + messagingController.synchronizeMailbox(account, folderId, notify, listener) + } + + fun checkMail( + id: AccountId?, + ignoreLastCheckedTime: Boolean, + useManualWakeLock: Boolean, + notify: Boolean, + listener: MessagingListener?, + ) { + val account = id?.let { getAccountDtoOrThrow(it) } + + messagingController.checkMail( + account, + ignoreLastCheckedTime, + useManualWakeLock, + notify, + listener, + ) + } + + fun supportsExpunge(id: AccountId): Boolean { + val account = getAccountDtoOrThrow(id) + return messagingController.supportsExpunge(account) + } + + fun isPushCapable(id: AccountId): Boolean { + val account = getAccountDtoOrThrow(id) + return messagingController.isPushCapable(account) + } + + fun markAllMessagesRead(id: AccountId, folderId: Long) { + val account = getAccountDtoOrThrow(id) + messagingController.markAllMessagesRead(account, folderId) + } + + fun checkAuthenticationProblem(id: AccountId) { + val account = getAccountDtoOrThrow(id) + messagingController.checkAuthenticationProblem(account) + } + + fun isMoveCapable(message: MessageReference) = messagingController.isMoveCapable(message) + fun isCopyCapable(message: MessageReference) = messagingController.isCopyCapable(message) + + fun deleteThreads(messages: List) = messagingController.deleteThreads(messages) + + fun deleteMessages(messages: List) = messagingController.deleteMessages(messages) + fun archiveThreads(messages: List) = messagingController.archiveThreads(messages) + fun archiveMessages(messages: List) = messagingController.archiveMessages(messages) +} diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt b/legacy/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt index 0852f38e2f9..c6bce5856db 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt +++ b/legacy/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt @@ -5,13 +5,13 @@ import com.fsck.k9.notification.NotificationController import com.fsck.k9.search.isNewMessages import com.fsck.k9.search.isSingleFolder import com.fsck.k9.search.isUnifiedFolders -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.search.legacy.LocalMessageSearch internal class NotificationOperations( private val notificationController: NotificationController, - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messageStoreManager: MessageStoreManager, ) { fun clearNotifications(search: LocalMessageSearch) { diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/push/PushController.kt b/legacy/core/src/main/java/com/fsck/k9/controller/push/PushController.kt index d715c07b531..8acd8e6e568 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/push/PushController.kt +++ b/legacy/core/src/main/java/com/fsck/k9/controller/push/PushController.kt @@ -20,8 +20,8 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.android.network.ConnectivityChangeListener import net.thunderbird.core.android.network.ConnectivityManager import net.thunderbird.core.logging.legacy.Log @@ -34,7 +34,7 @@ import net.thunderbird.core.preference.GeneralSettingsManager */ @Suppress("LongParameterList") class PushController internal constructor( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val generalSettingsManager: GeneralSettingsManager, private val backendManager: BackendManager, private val pushServiceManager: PushServiceManager, diff --git a/legacy/core/src/main/java/com/fsck/k9/job/K9JobManager.kt b/legacy/core/src/main/java/com/fsck/k9/job/K9JobManager.kt index 6c2b122899c..55ddb3634ee 100644 --- a/legacy/core/src/main/java/com/fsck/k9/job/K9JobManager.kt +++ b/legacy/core/src/main/java/com/fsck/k9/job/K9JobManager.kt @@ -3,13 +3,13 @@ package com.fsck.k9.job import androidx.work.WorkInfo import androidx.work.WorkManager import kotlinx.coroutines.flow.Flow -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log class K9JobManager( private val workManager: WorkManager, - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val mailSyncWorkerManager: MailSyncWorkerManager, private val syncDebugFileLogManager: FileLogLimitWorkManager, ) { diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/DefaultSpecialFolderUpdater.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/DefaultSpecialFolderUpdater.kt index 77ca1363d5a..3e76adad0b5 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/DefaultSpecialFolderUpdater.kt +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/DefaultSpecialFolderUpdater.kt @@ -142,7 +142,7 @@ class DefaultSpecialFolderUpdater private constructor( private val preferences: Preferences, private val folderRepository: FolderRepository, private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy, - ) : SpecialFolderUpdater.Factory { + ) : LegacyAccountDtoSpecialFolderUpdaterFactory { override fun create(account: LegacyAccountDto): SpecialFolderUpdater = DefaultSpecialFolderUpdater( preferences = preferences, folderRepository = folderRepository, diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/K9BackendStorageFactory.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/K9BackendStorageFactory.kt index ff4c3112b62..2f982a92f1a 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/K9BackendStorageFactory.kt +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/K9BackendStorageFactory.kt @@ -3,7 +3,6 @@ package com.fsck.k9.mailstore import app.k9mail.legacy.mailstore.FolderRepository import app.k9mail.legacy.mailstore.MessageStoreManager import com.fsck.k9.Preferences -import net.thunderbird.backend.api.BackendStorageFactory import net.thunderbird.core.android.account.LegacyAccountDto import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater @@ -13,7 +12,7 @@ class K9BackendStorageFactory( private val messageStoreManager: MessageStoreManager, private val specialFolderUpdaterFactory: SpecialFolderUpdater.Factory, private val saveMessageDataCreator: SaveMessageDataCreator, -) : BackendStorageFactory { +) : LegacyAccountDtoBackendStorageFactory { override fun createBackendStorage(account: LegacyAccountDto): K9BackendStorage { val messageStore = messageStoreManager.getMessageStore(account) val folderSettingsProvider = FolderSettingsProvider(preferences, account) diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/KoinModule.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/KoinModule.kt index b7ef0ba23b3..8f1adab2ab5 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/KoinModule.kt +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/KoinModule.kt @@ -6,8 +6,6 @@ import app.k9mail.legacy.mailstore.MessageStoreManager import com.fsck.k9.message.extractors.AttachmentCounter import com.fsck.k9.message.extractors.MessageFulltextCreator import com.fsck.k9.message.extractors.MessagePreviewCreator -import net.thunderbird.backend.api.BackendStorageFactory -import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater import org.koin.dsl.module val mailStoreModule = module { @@ -19,14 +17,7 @@ val mailStoreModule = module { single { MessageViewInfoExtractorFactory(get(), get(), get()) } single { AndroidStorageFilesProviderFactory(context = get()) } single { SpecialFolderSelectionStrategy() } - factory> { - DefaultSpecialFolderUpdater.Factory( - folderRepository = get(), - specialFolderSelectionStrategy = get(), - preferences = get(), - ) - } - single { + single { K9BackendStorageFactory( preferences = get(), folderRepository = get(), @@ -35,9 +26,6 @@ val mailStoreModule = module { saveMessageDataCreator = get(), ) } - single> { - get() - } factory { SpecialLocalFoldersCreator(preferences = get(), localStoreProvider = get()) } single { MessageStoreManager(accountManager = get(), messageStoreFactory = get()) } single { MessageRepository(messageStoreManager = get()) } diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/LegacyAccountDtoBackendStorageFactory.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/LegacyAccountDtoBackendStorageFactory.kt new file mode 100644 index 00000000000..0cbb43a09c7 --- /dev/null +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/LegacyAccountDtoBackendStorageFactory.kt @@ -0,0 +1,6 @@ +package com.fsck.k9.mailstore + +import net.thunderbird.backend.api.BackendStorageFactory +import net.thunderbird.core.android.account.LegacyAccountDto + +interface LegacyAccountDtoBackendStorageFactory : BackendStorageFactory diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/LegacyAccountDtoSpecialFolderUpdaterFactory.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/LegacyAccountDtoSpecialFolderUpdaterFactory.kt new file mode 100644 index 00000000000..c75f8a49179 --- /dev/null +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/LegacyAccountDtoSpecialFolderUpdaterFactory.kt @@ -0,0 +1,6 @@ +package com.fsck.k9.mailstore + +import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.feature.mail.folder.api.SpecialFolderUpdater + +interface LegacyAccountDtoSpecialFolderUpdaterFactory : SpecialFolderUpdater.Factory diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStoreProvider.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStoreProvider.kt index ab4827ccd72..414294f19d8 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStoreProvider.kt +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStoreProvider.kt @@ -3,9 +3,11 @@ package com.fsck.k9.mailstore import android.content.Context import app.k9mail.legacy.di.DI import java.util.concurrent.ConcurrentHashMap +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.account.LegacyAccountDto import net.thunderbird.core.common.exception.MessagingException import net.thunderbird.core.preference.GeneralSettingsManager +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper class LocalStoreProvider { private val localStores = ConcurrentHashMap() @@ -17,18 +19,36 @@ class LocalStoreProvider { val generalSettingsManager = DI.get(GeneralSettingsManager::class.java) val accountUuid = account.uuid + return getInstanceById(accountUuid) { + LocalStore.createInstance(account, context, generalSettingsManager) + } + } + + @Throws(MessagingException::class) + fun getInstanceByLegacyAccount(account: LegacyAccount): LocalStore { + val context = DI.get(Context::class.java) + val legacyAccountMapper = DI.get(LegacyAccountDataMapper::class.java) + val generalSettingsManager = DI.get(GeneralSettingsManager::class.java) + val accountUuid = account.uuid + val accountDto = legacyAccountMapper.toDto(account) + + return getInstanceById(accountUuid) { + LocalStore.createInstance(accountDto, context, generalSettingsManager) + } + } + + private fun getInstanceById(uuid: String, create: () -> LocalStore): LocalStore { // Use per-account locks so DatabaseUpgradeService always knows which account database is currently upgraded. - synchronized(accountLocks.getOrPut(accountUuid) { Any() }) { + synchronized(accountLocks.getOrPut(uuid) { Any() }) { // Creating a LocalStore instance will create or upgrade the database if // necessary. This could take some time. - return localStores.getOrPut(accountUuid) { - LocalStore.createInstance(account, context, generalSettingsManager) + return localStores.getOrPut(uuid) { + create() } } } - fun removeInstance(account: LegacyAccountDto) { - val accountUuid = account.uuid - localStores.remove(accountUuid) + fun removeInstance(uuid: String) { + localStores.remove(uuid) } } diff --git a/legacy/core/src/main/java/com/fsck/k9/notification/NotificationChannelManager.kt b/legacy/core/src/main/java/com/fsck/k9/notification/NotificationChannelManager.kt index 3fedc4dc54d..2c8a1870253 100644 --- a/legacy/core/src/main/java/com/fsck/k9/notification/NotificationChannelManager.kt +++ b/legacy/core/src/main/java/com/fsck/k9/notification/NotificationChannelManager.kt @@ -9,14 +9,14 @@ import android.os.Build import androidx.annotation.RequiresApi import androidx.core.net.toUri import java.util.concurrent.Executor -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log import net.thunderbird.feature.notification.NotificationLight import net.thunderbird.feature.notification.NotificationSettings class NotificationChannelManager( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val backgroundExecutor: Executor, private val notificationManager: NotificationManager, private val resourceProvider: NotificationResourceProvider, diff --git a/legacy/core/src/main/java/com/fsck/k9/preferences/KoinModule.kt b/legacy/core/src/main/java/com/fsck/k9/preferences/KoinModule.kt index 4e71aa5b9c7..d9662b4fd17 100644 --- a/legacy/core/src/main/java/com/fsck/k9/preferences/KoinModule.kt +++ b/legacy/core/src/main/java/com/fsck/k9/preferences/KoinModule.kt @@ -2,6 +2,7 @@ package com.fsck.k9.preferences import com.fsck.k9.Preferences import kotlin.time.ExperimentalTime +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.DebugLogConfigurator import net.thunderbird.core.preference.DefaultPreferenceChangeBroker import net.thunderbird.core.preference.GeneralSettingsManager @@ -27,12 +28,10 @@ import net.thunderbird.core.preference.notification.DefaultNotificationPreferenc import net.thunderbird.core.preference.notification.NotificationPreferenceManager import net.thunderbird.core.preference.privacy.DefaultPrivacySettingsPreferenceManager import net.thunderbird.core.preference.privacy.PrivacySettingsPreferenceManager -import net.thunderbird.feature.mail.account.api.AccountManager import org.koin.core.qualifier.named import org.koin.dsl.bind import org.koin.dsl.binds import org.koin.dsl.module -import net.thunderbird.core.android.account.AccountManager as LegacyAccountManager val preferencesModule = module { factory { @@ -46,8 +45,7 @@ val preferencesModule = module { ) } factory { FolderSettingsProvider(folderRepository = get()) } - factory { get() } - factory> { get() } + factory { get() } single { DefaultPrivacySettingsPreferenceManager( logger = get(), diff --git a/legacy/core/src/main/java/com/fsck/k9/preferences/UnifiedInboxConfigurator.kt b/legacy/core/src/main/java/com/fsck/k9/preferences/UnifiedInboxConfigurator.kt index a46cf4dae47..3d1559563c0 100644 --- a/legacy/core/src/main/java/com/fsck/k9/preferences/UnifiedInboxConfigurator.kt +++ b/legacy/core/src/main/java/com/fsck/k9/preferences/UnifiedInboxConfigurator.kt @@ -1,6 +1,6 @@ package com.fsck.k9.preferences -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.core.preference.update @@ -8,7 +8,7 @@ import net.thunderbird.core.preference.update * Configures the unified inbox after an account has been added. */ class UnifiedInboxConfigurator( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val generalSettingsManager: GeneralSettingsManager, ) { fun configureUnifiedInbox() { diff --git a/legacy/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt b/legacy/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt index b1145794c01..1a329fd6479 100644 --- a/legacy/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt +++ b/legacy/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt @@ -2,8 +2,10 @@ package com.fsck.k9.search -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager +import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.feature.search.legacy.LocalMessageSearch import net.thunderbird.feature.search.legacy.SearchAccount @@ -19,8 +21,9 @@ val LocalMessageSearch.isSingleAccount: Boolean val LocalMessageSearch.isSingleFolder: Boolean get() = isSingleAccount && folderIds.size == 1 +@Deprecated("Use getLegacyAccounts instead") @JvmName("getAccountsFromLocalSearch") -fun LocalMessageSearch.getAccounts(accountManager: AccountManager): List { +fun LocalMessageSearch.getAccounts(accountManager: LegacyAccountDtoManager): List { val accounts = accountManager.getAccounts() return if (searchAllAccounts()) { accounts @@ -30,6 +33,17 @@ fun LocalMessageSearch.getAccounts(accountManager: AccountManager): List { - return getAccounts(accountManager).map { it.uuid } +@JvmName("getLegacyAccountsFromLocalSearch") +fun LocalMessageSearch.getLegacyAccounts(accountManager: LegacyAccountManager): List { + val accounts = accountManager.getAccounts() + return if (searchAllAccounts()) { + accounts + } else { + val searchAccountUuids = accountUuids.toSet() + accounts.filter { it.uuid in searchAccountUuids } + } +} + +fun LocalMessageSearch.getLegacyAccountUuids(accountManager: LegacyAccountManager): List { + return getLegacyAccounts(accountManager).map { it.uuid } } diff --git a/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt b/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt index 98f4e140b7d..5b2e274fc65 100644 --- a/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt +++ b/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt @@ -3,7 +3,7 @@ package com.fsck.k9 import assertk.assertThat import assertk.assertions.isEqualTo import com.fsck.k9.preferences.UnifiedInboxConfigurator -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.preference.GeneralSettings import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.core.preference.display.DisplaySettings @@ -22,13 +22,13 @@ import org.mockito.junit.MockitoJUnitRunner @RunWith(MockitoJUnitRunner::class) class UnifiedInboxConfiguratorTest { - private lateinit var accountManager: AccountManager + private lateinit var accountManager: LegacyAccountDtoManager private lateinit var generalSettingsManager: GeneralSettingsManager private lateinit var configurator: UnifiedInboxConfigurator @Before fun setUp() { - accountManager = mock(AccountManager::class.java) + accountManager = mock(LegacyAccountDtoManager::class.java) generalSettingsManager = FakeGeneralSettingsManager( GeneralSettings( diff --git a/legacy/core/src/test/java/com/fsck/k9/controller/DefaultMessageCountsProviderTest.kt b/legacy/core/src/test/java/com/fsck/k9/controller/DefaultMessageCountsProviderTest.kt index 56651dc59d1..3baf2f53321 100644 --- a/legacy/core/src/test/java/com/fsck/k9/controller/DefaultMessageCountsProviderTest.kt +++ b/legacy/core/src/test/java/com/fsck/k9/controller/DefaultMessageCountsProviderTest.kt @@ -11,8 +11,8 @@ import assertk.assertThat import assertk.assertions.isEqualTo import kotlinx.coroutines.test.runTest import net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID_RAW -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.search.legacy.LocalMessageSearch import net.thunderbird.feature.search.legacy.SearchConditionTreeNode import org.junit.Test @@ -27,7 +27,7 @@ private const val STARRED_COUNT = 3 class DefaultMessageCountsProviderTest { private val account = LegacyAccountDto(ACCOUNT_ID_RAW) - private val accountManager = mock { + private val accountManager = mock { on { getAccounts() } doReturn listOf(account) } private val messageStore = mock { diff --git a/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageStoreManager.kt b/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageStoreManager.kt index 71cb730f464..a334a64f8c6 100644 --- a/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageStoreManager.kt +++ b/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageStoreManager.kt @@ -1,18 +1,19 @@ package app.k9mail.legacy.mailstore import java.util.concurrent.ConcurrentHashMap -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager +import net.thunderbird.feature.account.AccountId class MessageStoreManager( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messageStoreFactory: MessageStoreFactory, ) { - private val messageStores = ConcurrentHashMap() + private val messageStores = ConcurrentHashMap() init { - accountManager.addAccountRemovedListener { account -> - removeMessageStore(account.uuid) + accountManager.addAccountRemovedListener { accountId -> + removeMessageStore(accountId) } } @@ -22,10 +23,10 @@ class MessageStoreManager( } fun getMessageStore(account: LegacyAccountDto): ListenableMessageStore { - return messageStores.getOrPut(account.uuid) { messageStoreFactory.create(account) } + return messageStores.getOrPut(account.id) { messageStoreFactory.create(account) } } - private fun removeMessageStore(accountUuid: String) { - messageStores.remove(accountUuid) + private fun removeMessageStore(id: AccountId) { + messageStores.remove(id) } } diff --git a/legacy/mailstore/src/test/java/app/k9mail/legacy/mailstore/MessageStoreManagerTest.kt b/legacy/mailstore/src/test/java/app/k9mail/legacy/mailstore/MessageStoreManagerTest.kt index dd94ae51b14..84533a8a779 100644 --- a/legacy/mailstore/src/test/java/app/k9mail/legacy/mailstore/MessageStoreManagerTest.kt +++ b/legacy/mailstore/src/test/java/app/k9mail/legacy/mailstore/MessageStoreManagerTest.kt @@ -2,9 +2,9 @@ package app.k9mail.legacy.mailstore import assertk.assertThat import assertk.assertions.isSameInstanceAs -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.AccountRemovedListener import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import org.junit.Test import org.mockito.kotlin.KStubbing import org.mockito.kotlin.argumentCaptor @@ -23,7 +23,7 @@ class MessageStoreManagerTest { @Test fun `MessageStore instance is reused`() { - val accountManager = mock() + val accountManager = mock() val messageStoreManager = MessageStoreManager(accountManager, messageStoreFactory) assertThat(messageStoreManager.getMessageStore(account)).isSameInstanceAs(messageStore1) @@ -33,14 +33,14 @@ class MessageStoreManagerTest { @Test fun `MessageStore instance is removed when account is removed`() { val listenerCaptor = argumentCaptor() - val accountManager = mock { + val accountManager = mock { doNothingOn { addAccountRemovedListener(listenerCaptor.capture()) } } val messageStoreManager = MessageStoreManager(accountManager, messageStoreFactory) assertThat(messageStoreManager.getMessageStore(account)).isSameInstanceAs(messageStore1) - listenerCaptor.firstValue.onAccountRemoved(account) + listenerCaptor.firstValue.onAccountRemoved(account.id) assertThat(messageStoreManager.getMessageStore(account)).isSameInstanceAs(messageStore2) } diff --git a/legacy/message/src/main/java/app/k9mail/legacy/message/controller/MessagingControllerRegistry.kt b/legacy/message/src/main/java/app/k9mail/legacy/message/controller/MessagingControllerRegistry.kt index bbbc6cc93f7..fea0efb8410 100644 --- a/legacy/message/src/main/java/app/k9mail/legacy/message/controller/MessagingControllerRegistry.kt +++ b/legacy/message/src/main/java/app/k9mail/legacy/message/controller/MessagingControllerRegistry.kt @@ -1,5 +1,12 @@ package app.k9mail.legacy.message.controller +/** + * Interface for managing [MessagingListener] instances. + * + * Implementations of this registry allow components to register or unregister + * listeners that receive messaging events, enabling decoupled event handling + * within the messaging subsystem. + */ interface MessagingControllerRegistry { fun addListener(listener: MessagingListener) diff --git a/legacy/ui/folder/src/main/java/app/k9mail/legacy/ui/folder/DefaultDisplayFolderRepository.kt b/legacy/ui/folder/src/main/java/app/k9mail/legacy/ui/folder/DefaultDisplayFolderRepository.kt index 90caf860428..98010e17750 100644 --- a/legacy/ui/folder/src/main/java/app/k9mail/legacy/ui/folder/DefaultDisplayFolderRepository.kt +++ b/legacy/ui/folder/src/main/java/app/k9mail/legacy/ui/folder/DefaultDisplayFolderRepository.kt @@ -15,13 +15,13 @@ import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.mail.folder.api.Folder import net.thunderbird.feature.mail.folder.api.FolderType class DefaultDisplayFolderRepository( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val messagingController: MessagingControllerRegistry, private val messageStoreManager: MessageStoreManager, private val coroutineContext: CoroutineContext = Dispatchers.IO, diff --git a/legacy/ui/legacy/build.gradle.kts b/legacy/ui/legacy/build.gradle.kts index 3caa22f3ebb..5c44fd33075 100644 --- a/legacy/ui/legacy/build.gradle.kts +++ b/legacy/ui/legacy/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(projects.core.android.contact) implementation(projects.core.featureflag) + implementation(projects.core.logging.api) implementation(projects.core.ui.theme.api) implementation(projects.feature.launcher) implementation(projects.core.common) diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/account/AccountRemover.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/account/AccountRemover.kt index 26d2e8f47cf..16238fa5d67 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/account/AccountRemover.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/account/AccountRemover.kt @@ -52,7 +52,7 @@ class AccountRemover( // Ignore, this may lead to localStores on sd-cards that are currently not inserted to be left } - localStoreProvider.removeInstance(account) + localStoreProvider.removeInstance(account.uuid) } private fun removeBackend(account: LegacyAccountDto) { diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/FolderInfoHolder.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/FolderInfoHolder.kt index 28602889e49..b564c21cdeb 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/FolderInfoHolder.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/FolderInfoHolder.kt @@ -2,14 +2,14 @@ package com.fsck.k9.activity import app.k9mail.legacy.ui.folder.FolderNameFormatter import com.fsck.k9.mailstore.LocalFolder -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.feature.mail.folder.api.Folder import net.thunderbird.feature.mail.folder.api.FolderType class FolderInfoHolder( private val folderNameFormatter: FolderNameFormatter, localFolder: LocalFolder, - account: LegacyAccountDto, + account: LegacyAccount, ) { @JvmField val databaseId = localFolder.databaseId @@ -23,7 +23,7 @@ class FolderInfoHolder( @JvmField var moreMessages = localFolder.hasMoreMessages() - private fun getDisplayName(account: LegacyAccountDto, localFolder: LocalFolder): String { + private fun getDisplayName(account: LegacyAccount, localFolder: LocalFolder): String { val folderId = localFolder.databaseId val folder = Folder( id = folderId, @@ -36,7 +36,7 @@ class FolderInfoHolder( companion object { @JvmStatic - fun getFolderType(account: LegacyAccountDto, folderId: Long): FolderType { + fun getFolderType(account: LegacyAccount, folderId: Long): FolderType { return when (folderId) { account.inboxFolderId -> FolderType.INBOX account.outboxFolderId -> FolderType.OUTBOX diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt index 75cf0bbd943..838d6286d6d 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt @@ -55,14 +55,17 @@ import com.fsck.k9.ui.settings.SettingsActivity import com.fsck.k9.view.ViewSwitcher import com.fsck.k9.view.ViewSwitcher.OnSwitchCompleteListener import com.google.android.material.textview.MaterialTextView -import net.thunderbird.core.android.account.AccountManager +import kotlin.getValue +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.featureflag.FeatureFlagKey import net.thunderbird.core.featureflag.FeatureFlagProvider import net.thunderbird.core.logging.Logger import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.core.preference.SplitViewMode +import net.thunderbird.feature.account.storage.legacy.mapper.LegacyAccountDataMapper import net.thunderbird.feature.navigation.drawer.api.NavigationDrawer import net.thunderbird.feature.navigation.drawer.dropdown.DropDownDrawer import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount @@ -93,7 +96,7 @@ open class MessageList : OnSwitchCompleteListener { private val preferences: Preferences by inject() - private val accountManager: AccountManager by inject() + private val accountManager: LegacyAccountDtoManager by inject() private val defaultFolderProvider: DefaultFolderProvider by inject() private val accountRemover: BackgroundAccountRemover by inject() private val generalSettingsManager: GeneralSettingsManager by inject() @@ -103,6 +106,7 @@ open class MessageList : private val fundingManager: FundingManager by inject() private val featureFlagProvider: FeatureFlagProvider by inject() private val logger: Logger by inject() + private val legacyAccountDataMapper: LegacyAccountDataMapper by inject() private lateinit var actionBar: ActionBar private var searchView: SearchView? = null @@ -1144,8 +1148,9 @@ open class MessageList : MessageActions.actionReply(this, messageReference, true, decryptionResultForReply) } - override fun onCompose(account: LegacyAccountDto?) { - MessageActions.actionCompose(this, account) + override fun onCompose(account: LegacyAccount?) { + val accountDto = account?.let { legacyAccountDataMapper.toDto(account) } + MessageActions.actionCompose(this, accountDto) } override fun onBackStackChanged() { @@ -1193,7 +1198,7 @@ open class MessageList : return true } - override fun startSearch(query: String, account: LegacyAccountDto?, folderId: Long?): Boolean { + override fun startSearch(query: String, account: LegacyAccount?, folderId: Long?): Boolean { // If this search was started from a MessageList of a single folder, pass along that folder info // so that we can enable remote search. val appData = if (account != null && folderId != null) { @@ -1220,7 +1225,7 @@ open class MessageList : return super.startSupportActionMode(callback) } - override fun showThread(account: LegacyAccountDto, threadRootId: Long) { + override fun showThread(account: LegacyAccount, threadRootId: Long) { showMessageViewPlaceHolder() val tmpSearch = LocalMessageSearch().apply { diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/helper/DisplayAddressHelper.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/helper/DisplayAddressHelper.kt index fb8d0a07c5d..8643b03f606 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/helper/DisplayAddressHelper.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/helper/DisplayAddressHelper.kt @@ -1,9 +1,9 @@ package com.fsck.k9.ui.helper -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccount object DisplayAddressHelper { - fun shouldShowRecipients(account: LegacyAccountDto, folderId: Long): Boolean { + fun shouldShowRecipients(account: LegacyAccount, folderId: Long): Boolean { return when (folderId) { account.inboxFolderId -> false account.archiveFolderId -> false diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt index 9b9a39caccf..15fa45cba10 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt @@ -26,12 +26,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.common.mail.toEmailAddressOrNull import net.thunderbird.feature.mail.folder.api.Folder -@Suppress("TooManyFunctions") +@Suppress("TooManyFunctions", "LongParameterList") internal class MessageDetailsViewModel( private val resources: Resources, private val messageRepository: MessageRepository, @@ -40,7 +40,7 @@ internal class MessageDetailsViewModel( private val contactRepository: ContactRepository, private val contactPermissionResolver: ContactPermissionResolver, private val clipboardManager: ClipboardManager, - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val participantFormatter: MessageDetailsParticipantFormatter, private val folderNameFormatter: FolderNameFormatter, ) : ViewModel() { diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/KoinModule.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/KoinModule.kt index 4d4ac476fbb..02bb972a562 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/KoinModule.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/KoinModule.kt @@ -12,7 +12,7 @@ val messageListUiModule = module { factory { DefaultFolderProvider() } factory { MessageListLoader( - preferences = get(), + accountManager = get(), localStoreProvider = get(), messageListRepository = get(), messageHelper = get(), @@ -20,7 +20,11 @@ val messageListUiModule = module { ) } factory { - MessageListLiveDataFactory(messageListLoader = get(), preferences = get(), messageListRepository = get()) + MessageListLiveDataFactory( + messageListLoader = get(), + accountManager = get(), + messageListRepository = get(), + ) } single { SortTypeToastProvider() } } diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt index 03d91e85c74..ebee1c575c9 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt @@ -35,6 +35,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import app.k9mail.legacy.message.controller.MessageReference +import app.k9mail.legacy.message.controller.MessagingControllerRegistry import app.k9mail.legacy.message.controller.SimpleMessagingListener import app.k9mail.legacy.ui.folder.FolderNameFormatter import app.k9mail.ui.utils.itemtouchhelper.ItemTouchHelper @@ -44,13 +45,14 @@ import com.fsck.k9.Preferences import com.fsck.k9.activity.FolderInfoHolder import com.fsck.k9.activity.Search import com.fsck.k9.activity.misc.ContactPicture -import com.fsck.k9.controller.MessagingController +import com.fsck.k9.controller.MessagingControllerWrapper import com.fsck.k9.fragment.ConfirmationDialogFragment import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener import com.fsck.k9.helper.Utility import com.fsck.k9.helper.mapToSet import com.fsck.k9.mail.Flag -import com.fsck.k9.search.getAccounts +import com.fsck.k9.mailstore.LocalStoreProvider +import com.fsck.k9.search.getLegacyAccounts import com.fsck.k9.ui.R import com.fsck.k9.ui.changelog.RecentChangesActivity import com.fsck.k9.ui.changelog.RecentChangesViewModel @@ -67,28 +69,29 @@ import java.util.concurrent.Future import kotlin.time.Clock import kotlin.time.ExperimentalTime import kotlinx.collections.immutable.persistentSetOf +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import net.jcip.annotations.GuardedBy -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.Expunge import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.core.android.account.SortType import net.thunderbird.core.android.network.ConnectivityManager -import net.thunderbird.core.architecture.data.DataMapper import net.thunderbird.core.common.action.SwipeAction import net.thunderbird.core.common.exception.MessagingException import net.thunderbird.core.featureflag.FeatureFlagKey import net.thunderbird.core.featureflag.FeatureFlagProvider import net.thunderbird.core.featureflag.FeatureFlagResult +import net.thunderbird.core.logging.Logger import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.core.ui.theme.api.FeatureThemeProvider -import net.thunderbird.feature.account.storage.legacy.mapper.DefaultLegacyAccountWrapperDataMapper import net.thunderbird.feature.mail.message.list.domain.DomainContract import net.thunderbird.feature.mail.message.list.ui.dialog.SetupArchiveFolderDialogFragmentFactory import net.thunderbird.feature.notification.api.ui.InAppNotificationHost @@ -106,6 +109,8 @@ private const val MAXIMUM_MESSAGE_SORT_OVERRIDES = 3 private const val MINIMUM_CLICK_INTERVAL = 200L private const val RECENT_CHANGES_SNACKBAR_DURATION = 10 * 1000 +private const val TAG = "MessageListFragment" + class MessageListFragment : Fragment(), ConfirmationDialogFragmentListener, @@ -117,23 +122,22 @@ class MessageListFragment : private val generalSettingsManager: GeneralSettingsManager by inject() private val sortTypeToastProvider: SortTypeToastProvider by inject() private val folderNameFormatter: FolderNameFormatter by inject { parametersOf(requireContext()) } - private val messagingController: MessagingController by inject() - private val accountManager: AccountManager by inject() + private val messagingController: MessagingControllerWrapper by inject() + private val messagingControllerRegistry: MessagingControllerRegistry by inject() + private val accountManager: LegacyAccountManager by inject() private val connectivityManager: ConnectivityManager by inject() + private val localStoreProvider: LocalStoreProvider by inject() @OptIn(ExperimentalTime::class) private val clock: Clock by inject() private val setupArchiveFolderDialogFragmentFactory: SetupArchiveFolderDialogFragmentFactory by inject() - private val legacyAccountDataMapper: DataMapper< - LegacyAccount, - LegacyAccountDto, - > by inject() private val preferences: Preferences by inject() - private val buildSwipeActions: DomainContract.UseCase.BuildSwipeActions by inject { + private val buildSwipeActions: DomainContract.UseCase.BuildSwipeActions by inject { parametersOf(preferences.storage) } private val featureFlagProvider: FeatureFlagProvider by inject() private val featureThemeProvider: FeatureThemeProvider by inject() + private val logger: Logger by inject() private val handler = MessageListHandler(this) private val activityListener = MessageListActivityListener() @@ -165,7 +169,9 @@ class MessageListFragment : private lateinit var accountUuids: Array private var accounts: List = emptyList() - private var account: LegacyAccountDto? = null + + private var account: LegacyAccount? = null + private var currentFolder: FolderInfoHolder? = null private var remoteSearchFuture: Future<*>? = null private var extraSearchResults: List? = null @@ -277,7 +283,8 @@ class MessageListFragment : private fun restoreInstanceState(savedInstanceState: Bundle?) { if (savedInstanceState == null) return - activeMessages = savedInstanceState.getStringArray(STATE_ACTIVE_MESSAGES)?.map { MessageReference.parse(it)!! } + activeMessages = savedInstanceState.getStringArray(STATE_ACTIVE_MESSAGES) + ?.map { MessageReference.parse(it)!! } restoreSelectedMessages(savedInstanceState) isRemoteSearch = savedInstanceState.getBoolean(STATE_REMOTE_SEARCH_PERFORMED) val messageReferenceString = savedInstanceState.getString(STATE_ACTIVE_MESSAGE) @@ -298,7 +305,9 @@ class MessageListFragment : }!! allAccounts = localSearch.searchAllAccounts() - val searchAccounts = localSearch.getAccounts(accountManager).also(::updateAccountList) + val searchAccounts = localSearch.getLegacyAccounts(accountManager).also { + accounts = it + } if (searchAccounts.size == 1) { isSingleAccountMode = true val singleAccount = searchAccounts[0] @@ -313,8 +322,9 @@ class MessageListFragment : isSingleFolderMode = false if (isSingleAccountMode && localSearch.folderIds.size == 1) { try { + val account = checkNotNull(account) val folderId = localSearch.folderIds[0] - currentFolder = getFolderInfoHolder(folderId, account!!) + currentFolder = getFolderInfoHolder(account, folderId) isSingleFolderMode = true } catch (e: MessagingException) { return Error.FolderNotFound @@ -478,7 +488,6 @@ class MessageListFragment : adapter = adapter, listener = swipeListener, accounts = accounts, - legacyAccountDataMapper = legacyAccountDataMapper, ).also { messageListSwipeCallback = it }, ) itemTouchHelper.attachToRecyclerView(recyclerView) @@ -582,10 +591,10 @@ class MessageListFragment : private fun initializeSortSettings() { if (isSingleAccountMode) { - val account = this.account!! + val account = checkNotNull(this.account) sortType = account.sortType - sortAscending = account.isSortAscending(sortType) - sortDateAscending = account.isSortAscending(SortType.SORT_DATE) + sortAscending = account.sortAscending[sortType] ?: sortType.isDefaultAscending + sortDateAscending = account.sortAscending[SortType.SORT_DATE] ?: SortType.SORT_DATE.isDefaultAscending } else { sortType = K9.sortType sortAscending = K9.isSortAscending(sortType) @@ -605,16 +614,12 @@ class MessageListFragment : ) if (forceUpdate) { - updateAccountList(accounts = config.search.getAccounts(accountManager)) + accounts = config.search.getLegacyAccounts(accountManager) } viewModel.loadMessageList(config, forceUpdate) } - private fun updateAccountList(accounts: List) { - this.accounts = accounts.map(legacyAccountDataMapper::toDomain) - } - fun folderLoading(folderId: Long, loading: Boolean) { currentFolder?.let { if (it.databaseId == folderId) { @@ -665,7 +670,7 @@ class MessageListFragment : if (account == null || isUnifiedFolders || accountManager.getAccounts().size == 1) { null } else { - account.displayName + account.profile.name } } @@ -681,18 +686,19 @@ class MessageListFragment : } override fun onFooterClicked() { + val account = this.account ?: return val currentFolder = this.currentFolder ?: return if (currentFolder.moreMessages && !localSearch.isManualSearch) { val folderId = currentFolder.databaseId - messagingController.loadMoreMessages(account, folderId) + messagingController.loadMoreMessages(account.id, folderId) } else if (isRemoteSearch) { val additionalSearchResults = extraSearchResults ?: return if (additionalSearchResults.isEmpty()) return val loadSearchResults: List - val limit = account!!.remoteSearchNumResults + val limit = account.remoteSearchNumResults if (limit in 1 until additionalSearchResults.size) { extraSearchResults = additionalSearchResults.subList(limit, additionalSearchResults.size) loadSearchResults = additionalSearchResults.subList(0, limit) @@ -703,7 +709,7 @@ class MessageListFragment : } messagingController.loadSearchResults( - account, + account.id, currentFolder.databaseId, loadSearchResults, activityListener, @@ -742,7 +748,7 @@ class MessageListFragment : floatingActionButton = null if (isNewMessagesView && !requireActivity().isChangingConfigurations) { - messagingController.clearNewMessages(account) + account?.id?.let { messagingController.clearNewMessages(it) } } super.onDestroyView() @@ -782,8 +788,10 @@ class MessageListFragment : density = K9.messageListDensity, ) - private fun getFolderInfoHolder(folderId: Long, account: LegacyAccountDto): FolderInfoHolder { - val localFolder = MlfUtils.getOpenFolder(folderId, account) + private fun getFolderInfoHolder(account: LegacyAccount, folderId: Long): FolderInfoHolder { + val localStore = localStoreProvider.getInstanceByLegacyAccount(account) + val localFolder = localStore.getFolder(folderId) + localFolder.open() return FolderInfoHolder(folderNameFormatter, localFolder, account) } @@ -794,7 +802,7 @@ class MessageListFragment : hasConnectivity = connectivityManager.isNetworkAvailable() } - messagingController.addListener(activityListener) + messagingControllerRegistry.addListener(activityListener) updateTitle() } @@ -802,7 +810,7 @@ class MessageListFragment : override fun onPause() { super.onPause() - messagingController.removeListener(activityListener) + messagingControllerRegistry.removeListener(activityListener) } private fun goBack() { @@ -823,15 +831,16 @@ class MessageListFragment : } private fun onRemoteSearchRequested() { - val searchAccount = account!!.uuid val folderId = currentFolder!!.databaseId val queryString = localSearch.remoteSearchArguments isRemoteSearch = true swipeRefreshLayout?.isEnabled = false + val account = this.account ?: return + remoteSearchFuture = messagingController.searchRemoteMessages( - searchAccount, + account.id, folderId, queryString, null, @@ -852,19 +861,25 @@ class MessageListFragment : // FIXME: Don't save the changes in the UI thread private fun changeSort(sortType: SortType, sortAscending: Boolean?) { this.sortType = sortType - - val account = account + val account = this.account if (account != null) { - account.sortType = this.sortType - if (sortAscending == null) { - this.sortAscending = account.isSortAscending(this.sortType) - } else { - this.sortAscending = sortAscending + val resolvedAscending = sortAscending ?: (account.sortAscending[sortType] ?: sortType.isDefaultAscending) + this.sortAscending = resolvedAscending + + val newSortAscendingMap = account.sortAscending.toMutableMap().apply { + this[sortType] = resolvedAscending } - account.setSortAscending(this.sortType, this.sortAscending) - sortDateAscending = account.isSortAscending(SortType.SORT_DATE) - accountManager.saveAccount(account) + this.sortDateAscending = newSortAscendingMap[SortType.SORT_DATE] ?: SortType.SORT_DATE.isDefaultAscending + + val updatedAccount = account.copy( + sortType = sortType, + sortAscending = newSortAscendingMap, + ) + lifecycleScope.launch(Dispatchers.IO) { + accountManager.saveAccount(updatedAccount) + this@MessageListFragment.account = updatedAccount + } } else { K9.sortType = this.sortType if (sortAscending == null) { @@ -915,7 +930,7 @@ class MessageListFragment : private fun onExpunge() { currentFolder?.let { folderInfoHolder -> - messagingController.expunge(account, folderInfoHolder.databaseId) + account?.id?.let { messagingController.expunge(it, folderInfoHolder.databaseId) } } } @@ -1090,7 +1105,7 @@ class MessageListFragment : } private fun onSendPendingMessages() { - messagingController.sendPendingMessages(account, null) + account?.id?.let { messagingController.sendPendingMessages(it, null) } } private fun updateFooterText() { @@ -1197,10 +1212,10 @@ class MessageListFragment : val account = messageListItem.account if (showingThreadedList && messageListItem.threadCount > 1) { val threadRootId = messageListItem.threadRoot - messagingController.setFlagForThreads(account, listOf(threadRootId), flag, newState) + messagingController.setFlagForThreads(account.id, listOf(threadRootId), flag, newState) } else { val messageId = messageListItem.databaseId - messagingController.setFlag(account, listOf(messageId), flag, newState) + messagingController.setFlag(account.id, listOf(messageId), flag, newState) } computeBatchDirection() @@ -1209,9 +1224,9 @@ class MessageListFragment : private fun setFlagForSelected(flag: Flag, newState: Boolean) { if (adapter.selected.isEmpty()) return - val messageMap = mutableMapOf>() - val threadMap = mutableMapOf>() - val accounts = mutableSetOf() + val messageMap = mutableMapOf>() + val threadMap = mutableMapOf>() + val accounts = mutableSetOf() for (messageListItem in adapter.selectedMessages) { val account = messageListItem.account @@ -1228,11 +1243,11 @@ class MessageListFragment : for (account in accounts) { messageMap[account]?.let { messageIds -> - messagingController.setFlag(account, messageIds, flag, newState) + messagingController.setFlag(account.id, messageIds, flag, newState) } threadMap[account]?.let { threadRootIds -> - messagingController.setFlagForThreads(account, threadRootIds, flag, newState) + messagingController.setFlagForThreads(account.id, threadRootIds, flag, newState) } } @@ -1317,13 +1332,23 @@ class MessageListFragment : activeMessages = null if (messages.isNotEmpty()) { - MlfUtils.setLastSelectedFolder(accountManager, messages, destinationFolderId) + setLastSelectedFolder(messages, destinationFolderId) } action(destinationFolderId, messages) } } + private fun setLastSelectedFolder(messages: List, folderId: Long) { + val firstMessage = messages.firstOrNull() ?: return + val account = accountManager.getAccount(firstMessage.accountUuid) ?: return + accountManager.saveAccount( + account.copy( + lastSelectedFolderId = folderId, + ), + ) + } + private fun onArchive(message: MessageReference) { onArchive(listOf(message)) } @@ -1340,7 +1365,7 @@ class MessageListFragment : private fun groupMessagesByAccount( messages: List, - ): Map> { + ): Map> { return messages.groupBy { accountManager.getAccount(it.accountUuid)!! } } @@ -1365,11 +1390,11 @@ class MessageListFragment : private fun checkCopyOrMovePossible(messages: List, operation: FolderOperation): Boolean { if (messages.isEmpty()) return false - val account = accountManager.getAccount(messages.first().accountUuid) + val account = accountManager.getAccount(messages.first().accountUuid) ?: return false if (operation == FolderOperation.MOVE && - !messagingController.isMoveCapable(account) || + !messagingController.isMoveCapable(account.id) || operation == FolderOperation.COPY && - !messagingController.isCopyCapable(account) + !messagingController.isCopyCapable(account.id) ) { return false } @@ -1410,25 +1435,52 @@ class MessageListFragment : for ((folderId, messagesInFolder) in folderMap) { val account = accountManager.getAccount(messagesInFolder.first().accountUuid) + if (account == null) { + logger.debug(TAG) { + "Account for message ${messagesInFolder.first()} not found, skipping copy/move operation" + } + continue + } - if (operation == FolderOperation.MOVE) { - if (showingThreadedList) { - messagingController.moveMessagesInThread(account, folderId, messagesInFolder, destinationFolderId) - } else { - messagingController.moveMessages(account, folderId, messagesInFolder, destinationFolderId) + when (operation) { + FolderOperation.MOVE if showingThreadedList -> { + messagingController.moveMessagesInThread( + account.id, + folderId, + messagesInFolder, + destinationFolderId, + ) } - } else { - if (showingThreadedList) { - messagingController.copyMessagesInThread(account, folderId, messagesInFolder, destinationFolderId) - } else { - messagingController.copyMessages(account, folderId, messagesInFolder, destinationFolderId) + FolderOperation.MOVE -> { + messagingController.moveMessages( + account.id, + folderId, + messagesInFolder, + destinationFolderId, + ) + } + FolderOperation.COPY if showingThreadedList -> { + messagingController.copyMessagesInThread( + account.id, + folderId, + messagesInFolder, + destinationFolderId, + ) + } + FolderOperation.COPY -> { + messagingController.copyMessages( + account.id, + folderId, + messagesInFolder, + destinationFolderId, + ) } } } } private fun onMoveToDraftsFolder(messages: List) { - messagingController.moveToDraftsFolder(account, currentFolder!!.databaseId, messages) + account?.id?.let { messagingController.moveToDraftsFolder(it, currentFolder!!.databaseId, messages) } activeMessages = null } @@ -1450,11 +1502,11 @@ class MessageListFragment : } R.id.dialog_confirm_empty_spam -> { - messagingController.emptySpam(account, null) + account?.id?.let { messagingController.emptySpam(it) } } R.id.dialog_confirm_empty_trash -> { - messagingController.emptyTrash(account, null) + account?.id?.let { messagingController.emptyTrash(it) } } } } @@ -1490,14 +1542,14 @@ class MessageListFragment : private fun checkMail() { if (isSingleAccountMode && isSingleFolderMode) { val folderId = currentFolder!!.databaseId - messagingController.synchronizeMailbox(account, folderId, false, activityListener) - messagingController.sendPendingMessages(account, activityListener) + account?.id?.let { messagingController.synchronizeMailbox(it, folderId, false, activityListener) } + account?.id?.let { messagingController.sendPendingMessages(it, activityListener) } } else if (allAccounts) { messagingController.checkMail(null, true, true, false, activityListener) } else { for (accountUuid in accountUuids) { val account = accountManager.getAccount(accountUuid) - messagingController.checkMail(account, true, true, false, activityListener) + account?.id?.let { messagingController.checkMail(it, true, true, false, activityListener) } } } } @@ -1619,7 +1671,8 @@ class MessageListFragment : get() { if (localSearch.isManualSearch || isOutbox) return false - return if (!messagingController.isMoveCapable(account)) { + val accountId = account?.id + return if (accountId == null || !messagingController.isMoveCapable(accountId)) { // For POP3 accounts only the Inbox is a remote folder. isInbox } else { @@ -1632,7 +1685,7 @@ class MessageListFragment : private fun shouldShowExpungeAction(): Boolean { val account = this.account ?: return false - return account.expungePolicy == Expunge.EXPUNGE_MANUALLY && messagingController.supportsExpunge(account) + return account.expungePolicy == Expunge.EXPUNGE_MANUALLY && messagingController.supportsExpunge(account.id) } private fun onRemoteSearch() { @@ -1645,7 +1698,10 @@ class MessageListFragment : } private val isRemoteSearchAllowed: Boolean - get() = isManualSearch && !isRemoteSearch && isSingleFolderMode && messagingController.isPushCapable(account) + get() = isManualSearch && + !isRemoteSearch && + isSingleFolderMode && + (account?.id?.let { messagingController.isPushCapable(it) } == true) fun onSearchRequested(query: String): Boolean { val folderId = currentFolder?.databaseId @@ -1688,7 +1744,7 @@ class MessageListFragment : messageListItems .map { it.account } .toSet() - .forEach(messagingController::checkAuthenticationProblem) + .forEach { account -> messagingController.checkAuthenticationProblem(account.id) } resetActionMode() computeBatchDirection() @@ -1821,7 +1877,7 @@ class MessageListFragment : private fun markAllAsRead() { if (isMarkAllAsReadSupported) { - messagingController.markAllMessagesRead(account, currentFolder!!.databaseId) + account?.id?.let { messagingController.markAllMessagesRead(it, currentFolder!!.databaseId) } } } @@ -1934,7 +1990,7 @@ class MessageListFragment : } SwipeAction.Delete -> true - SwipeAction.Move -> !isOutbox && messagingController.isMoveCapable(item.account) + SwipeAction.Move -> !isOutbox && messagingController.isMoveCapable(item.account.id) SwipeAction.Spam -> !isOutbox && item.account.hasSpamFolder() && item.folderId != item.account.spamFolderId } } @@ -2151,7 +2207,7 @@ class MessageListFragment : return true } - private fun setContextCapabilities(account: LegacyAccountDto?, menu: Menu) { + private fun setContextCapabilities(account: LegacyAccount?, menu: Menu) { if (!isSingleAccountMode || account == null) { // We don't support cross-account copy/move operations right now menu.findItem(R.id.move).isVisible = false @@ -2181,11 +2237,11 @@ class MessageListFragment : menu.findItem(R.id.move_to_drafts).isVisible = true } } else { - if (!messagingController.isCopyCapable(account)) { + if (!messagingController.isCopyCapable(account.id)) { menu.findItem(R.id.copy).isVisible = false } - if (!messagingController.isMoveCapable(account)) { + if (!messagingController.isMoveCapable(account.id)) { menu.findItem(R.id.move).isVisible = false menu.findItem(R.id.archive).isVisible = false menu.findItem(R.id.spam).isVisible = false @@ -2305,11 +2361,11 @@ class MessageListFragment : interface MessageListFragmentListener { fun setMessageListProgressEnabled(enable: Boolean) fun setMessageListProgress(level: Int) - fun showThread(account: LegacyAccountDto, threadRootId: Long) + fun showThread(account: LegacyAccount, threadRootId: Long) fun openMessage(messageReference: MessageReference) fun setMessageListTitle(title: String, subtitle: String? = null) - fun onCompose(account: LegacyAccountDto?) - fun startSearch(query: String, account: LegacyAccountDto?, folderId: Long?): Boolean + fun onCompose(account: LegacyAccount?) + fun startSearch(query: String, account: LegacyAccount?, folderId: Long?): Boolean fun startSupportActionMode(callback: ActionMode.Callback): ActionMode? fun goBack() diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt index f55556cf588..e961574b734 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt @@ -2,10 +2,10 @@ package com.fsck.k9.ui.messagelist import app.k9mail.legacy.message.controller.MessageReference import com.fsck.k9.mail.Address -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccount data class MessageListItem( - val account: LegacyAccountDto, + val account: LegacyAccount, val subject: String?, val threadCount: Int, val messageDate: Long, diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt index 9807dcde897..7ff3b864261 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt @@ -5,12 +5,12 @@ import app.k9mail.legacy.mailstore.MessageMapper import app.k9mail.legacy.message.extractors.PreviewResult.PreviewType import com.fsck.k9.helper.MessageHelper import com.fsck.k9.ui.helper.DisplayAddressHelper -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.preference.GeneralSettingsManager class MessageListItemMapper( private val messageHelper: MessageHelper, - private val account: LegacyAccountDto, + private val account: LegacyAccount, private val generalSettingsManager: GeneralSettingsManager, ) : MessageMapper { @@ -58,7 +58,7 @@ class MessageListItemMapper( ) } - private fun createUniqueId(account: LegacyAccountDto, messageId: Long): Long { + private fun createUniqueId(account: LegacyAccount, messageId: Long): Long { return ((account.accountNumber + 1).toLong() shl 52) + messageId } } diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveData.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveData.kt index 0b5702991e0..6c243576ec3 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveData.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveData.kt @@ -3,16 +3,16 @@ package com.fsck.k9.ui.messagelist import androidx.lifecycle.LiveData import app.k9mail.legacy.mailstore.MessageListChangedListener import app.k9mail.legacy.mailstore.MessageListRepository -import com.fsck.k9.search.getAccountUuids +import com.fsck.k9.search.getLegacyAccountUuids import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountManager class MessageListLiveData( private val messageListLoader: MessageListLoader, - private val accountManager: AccountManager, + private val accountManager: LegacyAccountManager, private val messageListRepository: MessageListRepository, private val coroutineScope: CoroutineScope, val config: MessageListConfig, @@ -45,7 +45,7 @@ class MessageListLiveData( private fun registerMessageListChangedListenerAsync() { coroutineScope.launch(Dispatchers.IO) { - val accountUuids = config.search.getAccountUuids(accountManager) + val accountUuids = config.search.getLegacyAccountUuids(accountManager) for (accountUuid in accountUuids) { messageListRepository.addListener(accountUuid, messageListChangedListener) diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveDataFactory.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveDataFactory.kt index be641436f09..9b8076c2b37 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveDataFactory.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLiveDataFactory.kt @@ -1,15 +1,15 @@ package com.fsck.k9.ui.messagelist import app.k9mail.legacy.mailstore.MessageListRepository -import com.fsck.k9.Preferences import kotlinx.coroutines.CoroutineScope +import net.thunderbird.core.android.account.LegacyAccountManager class MessageListLiveDataFactory( private val messageListLoader: MessageListLoader, - private val preferences: Preferences, + private val accountManager: LegacyAccountManager, private val messageListRepository: MessageListRepository, ) { fun create(coroutineScope: CoroutineScope, config: MessageListConfig): MessageListLiveData { - return MessageListLiveData(messageListLoader, preferences, messageListRepository, coroutineScope, config) + return MessageListLiveData(messageListLoader, accountManager, messageListRepository, coroutineScope, config) } } diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt index b588107faae..c38f9103e4c 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt @@ -1,12 +1,12 @@ package com.fsck.k9.ui.messagelist import app.k9mail.legacy.mailstore.MessageListRepository -import com.fsck.k9.Preferences import com.fsck.k9.helper.MessageHelper import com.fsck.k9.mailstore.LocalStoreProvider import com.fsck.k9.mailstore.MessageColumns -import com.fsck.k9.search.getAccounts -import net.thunderbird.core.android.account.LegacyAccountDto +import com.fsck.k9.search.getLegacyAccounts +import net.thunderbird.core.android.account.LegacyAccount +import net.thunderbird.core.android.account.LegacyAccountManager import net.thunderbird.core.android.account.SortType import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager @@ -15,7 +15,7 @@ import net.thunderbird.feature.search.legacy.api.MessageSearchField import net.thunderbird.feature.search.legacy.sql.SqlWhereClause class MessageListLoader( - private val preferences: Preferences, + private val accountManager: LegacyAccountManager, private val localStoreProvider: LocalStoreProvider, private val messageListRepository: MessageListRepository, private val messageHelper: MessageHelper, @@ -34,7 +34,7 @@ class MessageListLoader( } private fun getMessageListInfo(config: MessageListConfig): MessageListInfo { - val accounts = config.search.getAccounts(preferences) + val accounts = config.search.getLegacyAccounts(accountManager) val messageListItems = accounts .flatMap { account -> loadMessageListForAccount(account, config) @@ -46,7 +46,7 @@ class MessageListLoader( return MessageListInfo(messageListItems, hasMoreMessages) } - private fun loadMessageListForAccount(account: LegacyAccountDto, config: MessageListConfig): List { + private fun loadMessageListForAccount(account: LegacyAccount, config: MessageListConfig): List { val accountUuid = account.uuid val threadId = getThreadId(config.search) val sortOrder = buildSortOrder(config) @@ -69,7 +69,7 @@ class MessageListLoader( } } - private fun buildSelection(account: LegacyAccountDto, config: MessageListConfig): Pair> { + private fun buildSelection(account: LegacyAccount, config: MessageListConfig): Pair> { val query = StringBuilder() val queryArgs = mutableListOf() @@ -170,11 +170,11 @@ class MessageListLoader( return this.sortedWith(comparator) } - private fun loadHasMoreMessages(accounts: List, folderIds: List): Boolean { + private fun loadHasMoreMessages(accounts: List, folderIds: List): Boolean { return if (accounts.size == 1 && folderIds.size == 1) { val account = accounts[0] val folderId = folderIds[0] - val localStore = localStoreProvider.getInstance(account) + val localStore = localStoreProvider.getInstanceByLegacyAccount(account) val localFolder = localStore.getFolder(folderId) localFolder.open() localFolder.hasMoreMessages() diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListSwipeCallback.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListSwipeCallback.kt index d4756838c7c..cb388d16930 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListSwipeCallback.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListSwipeCallback.kt @@ -20,8 +20,6 @@ import com.google.android.material.color.ColorRoles import com.google.android.material.textview.MaterialTextView import kotlin.math.abs import net.thunderbird.core.android.account.LegacyAccount -import net.thunderbird.core.android.account.LegacyAccountDto -import net.thunderbird.core.architecture.data.DataMapper import net.thunderbird.core.common.action.SwipeAction import net.thunderbird.core.common.action.SwipeActions import net.thunderbird.feature.mail.message.list.domain.DomainContract @@ -32,11 +30,10 @@ class MessageListSwipeCallback( context: Context, private val resourceProvider: SwipeResourceProvider, private val swipeActionSupportProvider: SwipeActionSupportProvider, - private val buildSwipeActions: DomainContract.UseCase.BuildSwipeActions, + private val buildSwipeActions: DomainContract.UseCase.BuildSwipeActions, private val adapter: MessageListAdapter, private val listener: MessageListSwipeListener, accounts: List, - private val legacyAccountDataMapper: DataMapper, ) : ItemTouchHelper.Callback() { private var swipeActions: Map = emptyMap() private val swipePadding = context.resources.getDimension(R.dimen.messageListSwipeIconPadding).toInt() @@ -196,11 +193,11 @@ class MessageListSwipeCallback( private fun Canvas.drawBackground(dX: Float, width: Int, height: Int, item: MessageListItem) { val swipeActionConfig = if (dX > 0) swipeRightConfig else swipeLeftConfig - if (swipeActionConfig[item.accountWrapper.uuid] == null) { + if (swipeActionConfig[item.account.uuid] == null) { error("drawBackground() called despite swipeActionConfig[item.accountWrapper] == null") } - backgroundColorPaint.color = swipeActionConfig.getValue(item.accountWrapper.uuid).backgroundColor + backgroundColorPaint.color = swipeActionConfig.getValue(item.account.uuid).backgroundColor drawRect( 0F, 0F, @@ -213,7 +210,7 @@ class MessageListSwipeCallback( private fun Canvas.drawLayout(dX: Float, width: Int, height: Int, item: MessageListItem) { val swipeRight = dX > 0 val swipeThresholdReached = abs(dX) > swipeThreshold - val account = item.accountWrapper + val account = item.account val swipeActionConfig = if (swipeRight) swipeRightConfig[account.uuid] else swipeLeftConfig[account.uuid] if (swipeActionConfig == null) { @@ -341,10 +338,8 @@ class MessageListSwipeCallback( fun buildSwipeActions(accounts: List): Map { return buildSwipeActions( accountUuids = accounts.map { it.uuid }.toSet(), - isIncomingServerPop3 = { account -> - legacyAccountDataMapper.toDomain(account).isIncomingServerPop3() - }, - hasArchiveFolder = { account -> legacyAccountDataMapper.toDomain(account).hasArchiveFolder() }, + isIncomingServerPop3 = { it.isIncomingServerPop3() }, + hasArchiveFolder = { it.hasArchiveFolder() }, ) } @@ -383,9 +378,6 @@ class MessageListSwipeCallback( private val ViewHolder.messageListItem: MessageListItem? get() = (this as? MessageViewHolder)?.uniqueId?.let { adapter.getItemById(it) } - - private val MessageListItem.accountWrapper: LegacyAccount - get() = legacyAccountDataMapper.toDomain(account) } fun interface SwipeActionSupportProvider { diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MlfUtils.java b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MlfUtils.java deleted file mode 100644 index 6d47c9a9d5a..00000000000 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MlfUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.fsck.k9.ui.messagelist; - - -import java.util.List; -import android.text.TextUtils; -import app.k9mail.legacy.di.DI; -import app.k9mail.legacy.message.controller.MessageReference; -import com.fsck.k9.helper.Utility; -import net.thunderbird.core.common.exception.MessagingException; -import com.fsck.k9.mailstore.LocalFolder; -import com.fsck.k9.mailstore.LocalStore; -import com.fsck.k9.mailstore.LocalStoreProvider; -import net.thunderbird.core.android.account.AccountManager; -import net.thunderbird.core.android.account.LegacyAccountDto; - - -public class MlfUtils { - - static LocalFolder getOpenFolder(long folderId, LegacyAccountDto account) throws MessagingException { - LocalStore localStore = DI.get(LocalStoreProvider.class).getInstance(account); - LocalFolder localFolder = localStore.getFolder(folderId); - localFolder.open(); - return localFolder; - } - - static void setLastSelectedFolder(AccountManager accountManager, List messages, long folderId) { - MessageReference firstMsg = messages.get(0); - LegacyAccountDto account = accountManager.getAccount(firstMsg.getAccountUuid()); - account.setLastSelectedFolderId(folderId); - } - - public static String buildSubject(String subjectFromCursor, String emptySubject, int threadCount) { - if (TextUtils.isEmpty(subjectFromCursor)) { - return emptySubject; - } else if (threadCount > 1) { - // If this is a thread, strip the RE/FW from the subject. "Be like Outlook." - return Utility.stripSubject(subjectFromCursor); - } - return subjectFromCursor; - } -} diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt index 3268841f42f..1ab2c3a517b 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt @@ -22,16 +22,17 @@ import app.k9mail.core.ui.legacy.designsystem.atom.icon.Icons import com.fsck.k9.FontSizes import com.fsck.k9.UiDensity import com.fsck.k9.contacts.ContactPictureLoader +import com.fsck.k9.helper.Utility import com.fsck.k9.mail.Address import com.fsck.k9.ui.R import com.fsck.k9.ui.helper.RelativeDateTimeFormatter import com.fsck.k9.ui.messagelist.MessageListAppearance import com.fsck.k9.ui.messagelist.MessageListItem -import com.fsck.k9.ui.messagelist.MlfUtils import com.google.android.material.textview.MaterialTextView import java.util.Locale import kotlin.math.max +@Suppress("TooManyFunctions") class MessageViewHolder( view: View, private val appearance: MessageListAppearance, @@ -82,11 +83,15 @@ class MessageViewHolder( val maybeBoldTypeface = if (isRead) Typeface.NORMAL else Typeface.BOLD val displayDate = relativeDateTimeFormatter.formatDate(messageDate) val displayThreadCount = if (appearance.showingThreadedList) threadCount else 0 - val subject = MlfUtils.buildSubject(subject, res.getString(R.string.general_no_subject), displayThreadCount) + val subject = buildSubject( + subject = subject, + noSubjectText = res.getString(R.string.general_no_subject), + threadCount = displayThreadCount, + ) if (appearance.showAccountChip) { val accountChipDrawable = chipView.drawable.mutate() - DrawableCompat.setTint(accountChipDrawable, account.chipColor) + DrawableCompat.setTint(accountChipDrawable, account.profile.color) chipView.setImageDrawable(accountChipDrawable) } @@ -150,6 +155,19 @@ class MessageViewHolder( } } + private fun buildSubject( + subject: String?, + noSubjectText: String, + threadCount: Int, + ): String = if (subject.isNullOrEmpty()) { + noSubjectText + } else if (threadCount > 1) { + // If this is a thread, strip the RE/FW from the subject. "Be like Outlook." + Utility.stripSubject(subject) + } else { + subject + } + private fun getPreview(isMessageEncrypted: Boolean, previewText: String): String { return if (isMessageEncrypted) { res.getString(R.string.preview_encrypted) diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt index 910a734af61..dd037df751b 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt @@ -55,8 +55,8 @@ import com.fsck.k9.ui.messageview.MessageCryptoPresenter.MessageCryptoMvpView import com.fsck.k9.ui.settings.account.AccountSettingsActivity import com.fsck.k9.ui.share.ShareIntentBuilder import java.util.Locale -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log import net.thunderbird.core.preference.GeneralSettingsManager import net.thunderbird.core.ui.theme.api.Theme @@ -72,7 +72,7 @@ class MessageViewFragment : private val themeManager: ThemeManager by inject() private val messageLoaderHelperFactory: MessageLoaderHelperFactory by inject() - private val accountManager: AccountManager by inject() + private val accountManager: LegacyAccountDtoManager by inject() private val messagingController: MessagingController by inject() private val shareIntentBuilder: ShareIntentBuilder by inject() private val generalSettingsManager: GeneralSettingsManager by inject() diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsViewModel.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsViewModel.kt index 7919b7bdf0b..93b14ff2ac2 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsViewModel.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/SettingsViewModel.kt @@ -9,11 +9,11 @@ import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager internal class SettingsViewModel( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : ViewModel() { val accounts = accountManager.getAccountsFlow().asLiveData() diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsViewModel.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsViewModel.kt index 4526ee0000a..837ce6bce1c 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsViewModel.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsViewModel.kt @@ -11,13 +11,13 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import net.thunderbird.core.android.account.AccountManager import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.feature.mail.folder.api.FolderType import net.thunderbird.feature.mail.folder.api.RemoteFolder class AccountSettingsViewModel( - private val accountManager: AccountManager, + private val accountManager: LegacyAccountDtoManager, private val folderRepository: FolderRepository, private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy, private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/export/SettingsExportViewModel.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/export/SettingsExportViewModel.kt index df052dc932c..7856b2bc19f 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/export/SettingsExportViewModel.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/export/SettingsExportViewModel.kt @@ -14,14 +14,14 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import net.thunderbird.core.android.account.AccountManager +import net.thunderbird.core.android.account.LegacyAccountDtoManager import net.thunderbird.core.logging.legacy.Log private typealias AccountUuid = String private typealias AccountNumber = Int class SettingsExportViewModel( - val accountManager: AccountManager, + val accountManager: LegacyAccountDtoManager, val settingsExporter: SettingsExporter, ) : ViewModel() { private val uiModelLiveData = MutableLiveData() diff --git a/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/messagelist/MessageListAdapterTest.kt b/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/messagelist/MessageListAdapterTest.kt index db22c48bd96..ba843fdcfcb 100644 --- a/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/messagelist/MessageListAdapterTest.kt +++ b/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/messagelist/MessageListAdapterTest.kt @@ -21,14 +21,22 @@ import com.fsck.k9.FontSizes.Companion.LARGE import com.fsck.k9.UiDensity import com.fsck.k9.contacts.ContactPictureLoader import com.fsck.k9.mail.Address +import com.fsck.k9.mail.AuthType +import com.fsck.k9.mail.ConnectionSecurity +import com.fsck.k9.mail.ServerSettings import com.fsck.k9.ui.R import com.fsck.k9.ui.helper.RelativeDateTimeFormatter import com.google.android.material.textview.MaterialTextView import kotlin.time.ExperimentalTime -import net.thunderbird.core.android.account.LegacyAccountDto +import net.thunderbird.core.android.account.Identity +import net.thunderbird.core.android.account.LegacyAccount import net.thunderbird.core.android.testing.RobolectricTest import net.thunderbird.core.featureflag.FeatureFlagResult import net.thunderbird.core.testing.TestClock +import net.thunderbird.feature.account.AccountIdFactory +import net.thunderbird.feature.account.storage.profile.AvatarDto +import net.thunderbird.feature.account.storage.profile.AvatarTypeDto +import net.thunderbird.feature.account.storage.profile.ProfileDto import org.junit.Test import org.mockito.kotlin.mock import org.robolectric.Robolectric @@ -429,9 +437,7 @@ class MessageListAdapterTest : RobolectricTest() { } fun createMessageListItem( - account: LegacyAccountDto = LegacyAccountDto( - SOME_ACCOUNT_UUID, - ), + account: LegacyAccount = createLegacyAccount(), subject: String? = "irrelevant", threadCount: Int = 0, messageDate: Long = 0L, @@ -474,6 +480,51 @@ class MessageListAdapterTest : RobolectricTest() { ) } + private fun createLegacyAccount(): LegacyAccount { + return LegacyAccount( + id = AccountIdFactory.of(SOME_ACCOUNT_UUID), + name = "irrelevant", + email = "irrelevant@example.com", + profile = createProfile(), + incomingServerSettings = createServerSettings(), + outgoingServerSettings = createServerSettings(), + identities = listOf( + Identity( + description = null, + name = "irrelevant", + email = "irrelevant@example.com", + ), + ), + ) + } + + private fun createProfile(): ProfileDto { + return ProfileDto( + id = AccountIdFactory.of(SOME_ACCOUNT_UUID), + name = "irrelevant", + color = 0xFF00FF, + avatar = AvatarDto( + avatarType = AvatarTypeDto.MONOGRAM, + avatarMonogram = "ab", + avatarImageUri = null, + avatarIconName = null, + ), + ) + } + + private fun createServerSettings(): ServerSettings { + return ServerSettings( + type = "imap", + host = "irrelevant", + port = 993, + connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED, + authenticationType = AuthType.PLAIN, + username = "irrelevant", + password = "irrelevant", + clientCertificateAlias = null, + ) + } + fun MessageListAdapter.createAndBindView(item: MessageListItem = createMessageListItem()): View { messages = listOf(item) val holder = onCreateViewHolder(LinearLayout(context), 0)