Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -19,12 +22,12 @@ internal val appCommonAccountModule = module {
featureAccountStorageLegacyModule,
)

single<LegacyAccountManager> {
single<AccountManager<LegacyAccount>> {
DefaultLegacyAccountManager(
accountManager = get(),
accountDataMapper = get(),
)
}
} binds arrayOf(LegacyAccountManager::class)

single<AccountProfileLocalDataSource> {
DefaultAccountProfileLocalDataSource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<LegacyAccount>> {
Expand All @@ -35,4 +35,34 @@ internal class DefaultLegacyAccountManager(
accountDataMapper.toDto(account),
)
}

override fun getAccounts(): List<LegacyAccount> {
return accountManager.getAccounts()
.map { account ->
accountDataMapper.toDomain(account)
}
}

override fun getAccountsFlow(): Flow<List<LegacyAccount>> = getAll()

override fun getAccount(accountUuid: String): LegacyAccount? {
val dto = accountManager.getAccount(accountUuid)
return dto?.let { accountDataMapper.toDomain(it) }
}

override fun getAccountFlow(accountUuid: String): Flow<LegacyAccount?> {
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))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -10,6 +11,7 @@ import org.koin.dsl.module
internal val appCommonFeatureModule = module {
includes(featureLauncherModule)
includes(featureNotificationModule)
includes(appCommonFeatureMailModule)

factory<FeatureLauncherExternalContract.AccountSetupFinishedLauncher> {
AccountSetupFinishedLauncher(
Expand Down
Original file line number Diff line number Diff line change
@@ -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<BaseAccount> {
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}")
}
}
}
Original file line number Diff line number Diff line change
@@ -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<BaseAccount> {
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)
}
}
Original file line number Diff line number Diff line change
@@ -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<BaseAccount> {
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}")
}
}
}
Original file line number Diff line number Diff line change
@@ -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<BackendStorageFactory<BaseAccount>> {
BaseAccountBackendStorageFactory(
legacyFactory = get(),
legacyMapper = get(),
)
}

factory<LegacyAccountDtoSpecialFolderUpdaterFactory> {
DefaultSpecialFolderUpdater.Factory(
folderRepository = get(),
specialFolderSelectionStrategy = get(),
preferences = get(),
)
}

factory<SpecialFolderUpdater.Factory<BaseAccount>> {
BaseAccountSpecialFolderUpdaterFactory(
legacyFactory = get(),
legacyMapper = get(),
)
}

single<BackendFactory<BaseAccount>> {
BaseAccountImapBackendFactory(
legacyFactory = get(),
legacyMapper = get(),
)
}

single<ImapRemoteFolderCreatorFactory> {
DefaultImapRemoteFolderCreatorFactory(
logger = get(),
backendFactory = get(),
)
}

single<RemoteFolderCreator.Factory> {
RemoteFolderCreatorResolver(
imapFactory = get(),
)
}
}
Original file line number Diff line number Diff line change
@@ -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<RemoteFolderCreationOutcome.Success, RemoteFolderCreationOutcome.Error> {
return Outcome.success(RemoteFolderCreationOutcome.Success.AlreadyExists)
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,28 @@ internal class FakeLegacyAccountManager(
}
}
}

override fun getAccounts(): List<LegacyAccount> {
TODO("Not yet implemented")
}

override fun getAccountsFlow(): Flow<List<LegacyAccount>> {
TODO("Not yet implemented")
}

override fun getAccount(accountUuid: String): LegacyAccount? {
TODO("Not yet implemented")
}

override fun getAccountFlow(accountUuid: String): Flow<LegacyAccount?> {
TODO("Not yet implemented")
}

override fun moveAccount(account: LegacyAccount, newPosition: Int) {
TODO("Not yet implemented")
}

override fun saveAccount(account: LegacyAccount) {
TODO("Not yet implemented")
}
}
Loading
Loading