Add account name setting

This adds the account name setting to the new account settings UI.

Depends on #9025

Resolves #8923

Add the existing account name setting

Account Settings - Add account name setting
Add the existing account name setting

./gradlew :legacy:core:testDebugUnitTest --tests "com.fsck.k9.PreferencesTest" :feature:account:settings:impl:testDebugUnitTest --tests "net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsStateTest" --tests "net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsViewModelTest" --tests "net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsScreenKtTest" --tests "net.thunderbird.feature.account.settings.impl.AccountSettingsModuleKtTest" --tests "net.thunderbird.feature.account.settings.impl.domain.usecase.UpdateGeneralPreferencesTest" --tests "net.thunderbird.feature.account.settings.impl.domain.usecase.GetGeneralPreferencesTest" :app-thunderbird:testFossDebugUnitTest --tests "net.thunderbird.android.DependencyInjectionTest" :app-k9mail:testFossDebugUnitTest --tests "app.k9mail.DependencyInjectionTest"

diff --git a/app-common/build.gradle.kts b/app-common/build.gradle.kts
index 0ef3941d31b..9338077e3eb 100644
--- a/app-common/build.gradle.kts
+++ b/app-common/build.gradle.kts
@@ -9,7 +9,7 @@ android {
 dependencies {
     api(projects.legacy.common)

-    implementation(projects.feature.account.core)
+    api(projects.feature.account.core)

     implementation(projects.legacy.core)
     implementation(projects.legacy.account)
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/CommonAccountProfileLocalDataSource.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/CommonAccountProfileLocalDataSource.kt
index 7ea0c60b085..a73caf741ef 100644
--- a/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/CommonAccountProfileLocalDataSource.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/data/CommonAccountProfileLocalDataSource.kt
@@ -4,7 +4,6 @@ import app.k9mail.legacy.account.LegacyAccountWrapperManager
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
 import net.thunderbird.feature.account.api.AccountId
 import net.thunderbird.feature.account.api.profile.AccountProfile
 import net.thunderbird.feature.account.core.AccountCoreExternalContract.AccountProfileLocalDataSource
@@ -15,7 +14,6 @@ class CommonAccountProfileLocalDataSource(

     override fun getById(accountId: AccountId): Flow<AccountProfile?> {
         return accountManager.getById(accountId.value)
-            .onEach { println("Flow emitted account: $it") }
             .map { account ->
                 account?.let {
                     AccountProfile(
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt
index 59d459b2952..1b818674060 100644
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt
@@ -4,7 +4,6 @@ import android.os.Parcelable
 import androidx.compose.ui.graphics.vector.ImageVector
 import kotlinx.parcelize.IgnoredOnParcel
 import kotlinx.parcelize.Parcelize
-import kotlinx.parcelize.RawValue

 /**
  * A preference that can be displayed in a preference screen.
@@ -22,9 +21,9 @@ sealed interface PreferenceSetting<T> : Preference {
     @Parcelize
     data class Text(
         override val id: String,
-        val title: String,
-        val description: String? = null,
-        val icon: @RawValue ImageVector? = null,
+        val title: () -> String,
+        val description: () -> String? = { null },
+        val icon: () -> ImageVector? = { null },
         override val value: String,
     ) : PreferenceSetting<String> {
         @IgnoredOnParcel
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogTextView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogTextView.kt
index 2521887c2ad..7866e278866 100644
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogTextView.kt
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogTextView.kt
@@ -23,8 +23,8 @@ internal fun PreferenceDialogTextView(
     val currentPreference = remember { mutableStateOf(preference) }

     PreferenceDialogLayout(
-        title = preference.title,
-        icon = preference.icon,
+        title = preference.title(),
+        icon = preference.icon(),
         onConfirmClick = {
             onConfirmClick(currentPreference.value)
         },
@@ -32,7 +32,7 @@ internal fun PreferenceDialogTextView(
         onDismissRequest = onDismissRequest,
         modifier = modifier,
     ) {
-        preference.description?.let {
+        preference.description()?.let {
             TextBodyMedium(text = it)

             Spacer(modifier = Modifier.height(MainTheme.spacings.default))
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt
index a04011f88e0..a07672cfb84 100644
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt
@@ -14,10 +14,10 @@ internal fun PreferenceItemTextView(
 ) {
     PreferenceItemLayout(
         onClick = onClick,
-        icon = preference.icon,
+        icon = preference.icon(),
         modifier = modifier,
     ) {
-        TextTitleMedium(text = preference.title)
+        TextTitleMedium(text = preference.title())
         TextBodyMedium(text = preference.value)
     }
 }
diff --git a/feature/account/core/src/main/kotlin/net/thunderbird/feature/account/core/data/DefaultAccountProfileRepository.kt b/feature/account/core/src/main/kotlin/net/thunderbird/feature/account/core/data/DefaultAccountProfileRepository.kt
index c34549d2d81..cc5f2728e71 100644
--- a/feature/account/core/src/main/kotlin/net/thunderbird/feature/account/core/data/DefaultAccountProfileRepository.kt
+++ b/feature/account/core/src/main/kotlin/net/thunderbird/feature/account/core/data/DefaultAccountProfileRepository.kt
@@ -5,18 +5,18 @@ import kotlinx.coroutines.flow.distinctUntilChanged
 import net.thunderbird.feature.account.api.AccountId
 import net.thunderbird.feature.account.api.profile.AccountProfile
 import net.thunderbird.feature.account.api.profile.AccountProfileRepository
-import net.thunderbird.feature.account.core.AccountCoreExternalContract
+import net.thunderbird.feature.account.core.AccountCoreExternalContract.AccountProfileLocalDataSource

 class DefaultAccountProfileRepository(
-    private val localStore: AccountCoreExternalContract.AccountProfileLocalDataSource,
+    private val localDataSource: AccountProfileLocalDataSource,
 ) : AccountProfileRepository {

     override fun getById(accountId: AccountId): Flow<AccountProfile?> {
-        return localStore.getById(accountId)
+        return localDataSource.getById(accountId)
             .distinctUntilChanged()
     }

     override suspend fun update(accountProfile: AccountProfile) {
-        localStore.update(accountProfile)
+        localDataSource.update(accountProfile)
     }
 }
diff --git a/feature/account/settings/impl/build.gradle.kts b/feature/account/settings/impl/build.gradle.kts
index 4ebd2f9bc29..68351f2301d 100644
--- a/feature/account/settings/impl/build.gradle.kts
+++ b/feature/account/settings/impl/build.gradle.kts
@@ -9,6 +9,9 @@ android {

 dependencies {
     api(projects.feature.account.settings.api)
+    implementation(projects.feature.account.core)
+
+    implementation(projects.core.outcome)

     implementation(projects.core.ui.compose.designsystem)
     implementation(projects.core.ui.compose.navigation)
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/AccountSettingsModule.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/AccountSettingsModule.kt
index b97e32674bf..328830ffad0 100644
--- a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/AccountSettingsModule.kt
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/AccountSettingsModule.kt
@@ -2,8 +2,43 @@ package net.thunderbird.feature.account.settings

 import net.thunderbird.feature.account.settings.api.AccountSettingsNavigation
 import net.thunderbird.feature.account.settings.impl.DefaultAccountSettingsNavigation
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.UseCase
+import net.thunderbird.feature.account.settings.impl.domain.usecase.GetGeneralPreferences
+import net.thunderbird.feature.account.settings.impl.domain.usecase.UpdateGeneralPreferences
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralResourceProvider
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsViewModel
+import org.koin.android.ext.koin.androidContext
+import org.koin.androidx.viewmodel.dsl.viewModel
 import org.koin.dsl.module

 val featureAccountSettingsModule = module {
     single<AccountSettingsNavigation> { DefaultAccountSettingsNavigation() }
+
+    factory<ResourceProvider.GeneralResourceProvider> {
+        GeneralResourceProvider(
+            context = androidContext(),
+        )
+    }
+
+    factory<UseCase.GetGeneralPreferences> {
+        GetGeneralPreferences(
+            repository = get(),
+            resourceProvider = get(),
+        )
+    }
+
+    factory<UseCase.UpdateGeneralPreferences> {
+        UpdateGeneralPreferences(
+            repository = get(),
+        )
+    }
+
+    viewModel { params ->
+        GeneralSettingsViewModel(
+            accountId = params.get(),
+            getGeneralPreferences = get(),
+            updateGeneralPreferences = get(),
+        )
+    }
 }
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/DefaultAccountSettingsNavigation.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/DefaultAccountSettingsNavigation.kt
index d96817e32c4..99bbfaf5da3 100644
--- a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/DefaultAccountSettingsNavigation.kt
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/DefaultAccountSettingsNavigation.kt
@@ -3,11 +3,12 @@ package net.thunderbird.feature.account.settings.impl
 import androidx.navigation.NavGraphBuilder
 import androidx.navigation.toRoute
 import app.k9mail.core.ui.compose.navigation.deepLinkComposable
+import net.thunderbird.feature.account.api.AccountId
 import net.thunderbird.feature.account.settings.api.AccountSettingsNavigation
 import net.thunderbird.feature.account.settings.api.AccountSettingsRoute
-import net.thunderbird.feature.account.settings.impl.ui.AccountSettingsScreen
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsScreen

-class DefaultAccountSettingsNavigation : AccountSettingsNavigation {
+internal class DefaultAccountSettingsNavigation : AccountSettingsNavigation {

     override fun registerRoutes(
         navGraphBuilder: NavGraphBuilder,
@@ -19,9 +20,10 @@ class DefaultAccountSettingsNavigation : AccountSettingsNavigation {
                 basePath = AccountSettingsRoute.GeneralSettings.Companion.BASE_PATH,
             ) { backStackEntry ->
                 val generalSettingsRoute = backStackEntry.toRoute<AccountSettingsRoute.GeneralSettings>()
+                val accountId = AccountId.from(generalSettingsRoute.accountId)

-                AccountSettingsScreen(
-                    accountId = generalSettingsRoute.accountId,
+                GeneralSettingsScreen(
+                    accountId = accountId,
                     onBack = onBack,
                 )
             }
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/AccountSettingsDomainContract.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/AccountSettingsDomainContract.kt
new file mode 100644
index 00000000000..b756a85f4c4
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/AccountSettingsDomainContract.kt
@@ -0,0 +1,43 @@
+package net.thunderbird.feature.account.settings.impl.domain
+
+import androidx.compose.ui.graphics.vector.ImageVector
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.coroutines.flow.Flow
+import net.thunderbird.core.outcome.Outcome
+import net.thunderbird.core.ui.compose.preference.api.Preference
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.SettingsError
+
+internal typealias AccountSettingsOutcome = Outcome<ImmutableList<Preference>, SettingsError>
+
+internal interface AccountSettingsDomainContract {
+
+    interface UseCase {
+
+        fun interface GetGeneralPreferences {
+            operator fun invoke(accountId: AccountId): Flow<AccountSettingsOutcome>
+        }
+
+        fun interface UpdateGeneralPreferences {
+            suspend operator fun invoke(
+                accountId: AccountId,
+                preference: PreferenceSetting<*>,
+            ): Outcome<Unit, SettingsError>
+        }
+    }
+
+    interface ResourceProvider {
+        interface GeneralResourceProvider {
+            val nameTitle: () -> String
+            val nameDescription: () -> String?
+            val nameIcon: () -> ImageVector?
+        }
+    }
+
+    sealed interface SettingsError {
+        data class NotFound(
+            val message: String,
+        ) : SettingsError
+    }
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/entity/GeneralPreference.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/entity/GeneralPreference.kt
new file mode 100644
index 00000000000..ccc3017528f
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/entity/GeneralPreference.kt
@@ -0,0 +1,11 @@
+package net.thunderbird.feature.account.settings.impl.domain.entity
+
+import net.thunderbird.feature.account.api.AccountId
+
+internal enum class GeneralPreference {
+    NAME,
+}
+
+internal fun GeneralPreference.generateId(accountId: AccountId): String {
+    return "${accountId.value}-general-${this.name.lowercase()}"
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/GetGeneralPreferences.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/GetGeneralPreferences.kt
new file mode 100644
index 00000000000..053b6868c7c
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/GetGeneralPreferences.kt
@@ -0,0 +1,49 @@
+package net.thunderbird.feature.account.settings.impl.domain.usecase
+
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import net.thunderbird.core.outcome.Outcome
+import net.thunderbird.core.ui.compose.preference.api.Preference
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.api.profile.AccountProfile
+import net.thunderbird.feature.account.api.profile.AccountProfileRepository
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.SettingsError
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.UseCase
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsOutcome
+import net.thunderbird.feature.account.settings.impl.domain.entity.GeneralPreference
+import net.thunderbird.feature.account.settings.impl.domain.entity.generateId
+
+internal class GetGeneralPreferences(
+    private val repository: AccountProfileRepository,
+    private val resourceProvider: ResourceProvider.GeneralResourceProvider,
+) : UseCase.GetGeneralPreferences {
+    override fun invoke(accountId: AccountId): Flow<AccountSettingsOutcome> {
+        return repository.getById(accountId).map { profile ->
+            if (profile != null) {
+                Outcome.success(generatePreferences(accountId, profile))
+            } else {
+                Outcome.failure(
+                    SettingsError.NotFound(
+                        message = "Account profile not found for accountId: ${accountId.value}",
+                    ),
+                )
+            }
+        }
+    }
+
+    private fun generatePreferences(accountId: AccountId, profile: AccountProfile): ImmutableList<Preference> {
+        return persistentListOf(
+            PreferenceSetting.Text(
+                id = GeneralPreference.NAME.generateId(accountId),
+                title = resourceProvider.nameTitle,
+                description = resourceProvider.nameDescription,
+                icon = resourceProvider.nameIcon,
+                value = profile.name,
+            ),
+        )
+    }
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/UpdateGeneralPreferences.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/UpdateGeneralPreferences.kt
new file mode 100644
index 00000000000..041c938cac0
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/UpdateGeneralPreferences.kt
@@ -0,0 +1,52 @@
+package net.thunderbird.feature.account.settings.impl.domain.usecase
+
+import kotlinx.coroutines.flow.firstOrNull
+import net.thunderbird.core.outcome.Outcome
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.api.profile.AccountProfile
+import net.thunderbird.feature.account.api.profile.AccountProfileRepository
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.SettingsError
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.UseCase
+import net.thunderbird.feature.account.settings.impl.domain.entity.GeneralPreference
+import net.thunderbird.feature.account.settings.impl.domain.entity.generateId
+
+internal class UpdateGeneralPreferences(
+    private val repository: AccountProfileRepository,
+) : UseCase.UpdateGeneralPreferences {
+    override suspend fun invoke(
+        accountId: AccountId,
+        preference: PreferenceSetting<*>,
+    ): Outcome<Unit, SettingsError> {
+        return when (preference.id) {
+            GeneralPreference.NAME.generateId(accountId) -> {
+                updateAccountProfile(accountId) {
+                    copy(name = preference.value as String)
+                }
+            }
+
+            else -> Outcome.failure(
+                SettingsError.NotFound(
+                    message = "Unknown preference id: ${preference.id}",
+                ),
+            )
+        }
+    }
+
+    private suspend fun updateAccountProfile(
+        accountId: AccountId,
+        update: AccountProfile.() -> AccountProfile,
+    ): Outcome<Unit, SettingsError> {
+        val accountProfile = repository.getById(accountId).firstOrNull()
+            ?: return Outcome.failure(
+                SettingsError.NotFound(
+                    message = "Account profile not found for accountId: $accountId",
+                ),
+            )
+        val updatedAccountProfile = update(accountProfile)
+
+        repository.update(updatedAccountProfile)
+
+        return Outcome.success(Unit)
+    }
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/AccountSettingsScreen.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/AccountSettingsScreen.kt
deleted file mode 100644
index b5f4823998c..00000000000
--- a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/AccountSettingsScreen.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package net.thunderbird.feature.account.settings.impl.ui
-
-import androidx.activity.compose.BackHandler
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
-import kotlinx.collections.immutable.toImmutableList
-import net.thunderbird.core.ui.compose.preference.api.Preference
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-import net.thunderbird.core.ui.compose.preference.ui.PreferenceView
-
-@Composable
-fun AccountSettingsScreen(
-    accountId: String,
-    onBack: () -> Unit,
-    modifier: Modifier = Modifier,
-) {
-    val preferences = remember {
-        mutableStateOf(
-            listOf<Preference>(
-                PreferenceSetting.Text(
-                    id = "1",
-                    title = "Title 1",
-                    description = "Description 1",
-                    icon = Icons.Outlined.Delete,
-                    value = "Value 1",
-                ),
-                PreferenceSetting.Text(
-                    id = "2",
-                    title = "Title 2",
-                    description = "Description 2",
-                    icon = Icons.Outlined.Delete,
-                    value = "Value 2",
-                ),
-                PreferenceSetting.Text(
-                    id = "3",
-                    title = "Title 3",
-                    description = "Description 3",
-                    icon = Icons.Outlined.Folder,
-                    value = "Value 3",
-                ),
-            ),
-        )
-    }
-
-    BackHandler(onBack = onBack)
-
-    PreferenceView(
-        title = "Account settings",
-        subtitle = accountId,
-        preferences = preferences.value.toImmutableList(),
-        onPreferenceChange = { preference ->
-            preferences.value = updatePreference(
-                preferences = preferences.value,
-                preference = preference,
-            )
-        },
-        onBack = onBack,
-        modifier = modifier,
-    )
-}
-
-private fun updatePreference(
-    preferences: List<Preference>,
-    preference: PreferenceSetting<*>,
-): List<Preference> {
-    return preferences.map {
-        if (it is PreferenceSetting<*> && it.id == preference.id) {
-            preference
-        } else {
-            it
-        }
-    }
-}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralResourceProvider.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralResourceProvider.kt
new file mode 100644
index 00000000000..71bad15153c
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralResourceProvider.kt
@@ -0,0 +1,17 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import android.content.Context
+import androidx.compose.ui.graphics.vector.ImageVector
+import net.thunderbird.feature.account.settings.R
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
+
+internal class GeneralResourceProvider(
+    private val context: Context,
+) : ResourceProvider.GeneralResourceProvider {
+
+    override val nameTitle: () -> String = {
+        context.getString(R.string.account_settings_general_name_title)
+    }
+    override val nameDescription: () -> String? = { null }
+    override val nameIcon: () -> ImageVector? = { null }
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContent.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContent.kt
new file mode 100644
index 00000000000..e75200a10cc
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContent.kt
@@ -0,0 +1,27 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import net.thunderbird.core.ui.compose.preference.ui.PreferenceView
+import net.thunderbird.feature.account.settings.R
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Event
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.State
+
+@Composable
+internal fun GeneralSettingsContent(
+    state: State,
+    onEvent: (Event) -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    PreferenceView(
+        title = stringResource(R.string.account_settings_general_title),
+        subtitle = state.subtitle,
+        preferences = state.preferences,
+        onPreferenceChange = { preference ->
+            onEvent(Event.OnPreferenceSettingChange(preference))
+        },
+        onBack = { onEvent(Event.OnBackPressed) },
+        modifier = modifier,
+    )
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContract.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContract.kt
new file mode 100644
index 00000000000..a7c2b333f1e
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContract.kt
@@ -0,0 +1,31 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import androidx.compose.runtime.Stable
+import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
+import net.thunderbird.core.ui.compose.preference.api.Preference
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+
+internal interface GeneralSettingsContract {
+
+    interface ViewModel : UnidirectionalViewModel<State, Event, Effect>
+
+    @Stable
+    data class State(
+        val subtitle: String? = null,
+        val preferences: ImmutableList<Preference> = persistentListOf<Preference>(),
+    )
+
+    sealed interface Event {
+        data class OnPreferenceSettingChange(
+            val preference: PreferenceSetting<*>,
+        ) : Event
+
+        data object OnBackPressed : Event
+    }
+
+    sealed interface Effect {
+        object NavigateBack : Effect
+    }
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsScreen.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsScreen.kt
new file mode 100644
index 00000000000..e7111dead6f
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsScreen.kt
@@ -0,0 +1,31 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.runtime.Composable
+import app.k9mail.core.ui.compose.common.mvi.observe
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Effect
+import org.koin.androidx.compose.koinViewModel
+import org.koin.core.parameter.parametersOf
+
+@Composable
+internal fun GeneralSettingsScreen(
+    accountId: AccountId,
+    onBack: () -> Unit,
+    viewModel: GeneralSettingsContract.ViewModel = koinViewModel<GeneralSettingsViewModel> {
+        parametersOf(accountId)
+    },
+) {
+    val (state, dispatch) = viewModel.observe { effect ->
+        when (effect) {
+            is Effect.NavigateBack -> onBack()
+        }
+    }
+
+    BackHandler(onBack = onBack)
+
+    GeneralSettingsContent(
+        state = state.value,
+        onEvent = { dispatch(it) },
+    )
+}
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsViewModel.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsViewModel.kt
new file mode 100644
index 00000000000..6819e5dd7d0
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsViewModel.kt
@@ -0,0 +1,50 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import androidx.lifecycle.viewModelScope
+import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import kotlinx.coroutines.launch
+import net.thunderbird.core.outcome.handle
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.UseCase
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Effect
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Event
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.State
+
+internal class GeneralSettingsViewModel(
+    private val accountId: AccountId,
+    private val getGeneralPreferences: UseCase.GetGeneralPreferences,
+    private val updateGeneralPreferences: UseCase.UpdateGeneralPreferences,
+    initialState: State = State(),
+) : BaseViewModel<State, Event, Effect>(initialState), GeneralSettingsContract.ViewModel {
+
+    init {
+        viewModelScope.launch {
+            getGeneralPreferences(accountId).collect { outcome ->
+                outcome.handle(
+                    onSuccess = { preferences ->
+                        updateState { state ->
+                            state.copy(
+                                preferences = preferences,
+                            )
+                        }
+                    },
+                    onFailure = {},
+                )
+            }
+        }
+    }
+
+    override fun event(event: Event) {
+        when (event) {
+            is Event.OnPreferenceSettingChange -> updatePreference(event.preference)
+            is Event.OnBackPressed -> emitEffect(Effect.NavigateBack)
+        }
+    }
+
+    private fun updatePreference(preference: PreferenceSetting<*>) {
+        viewModelScope.launch {
+            updateGeneralPreferences(accountId, preference)
+        }
+    }
+}
diff --git a/feature/account/settings/impl/src/main/res/values/strings.xml b/feature/account/settings/impl/src/main/res/values/strings.xml
new file mode 100644
index 00000000000..7c0c8f030ee
--- /dev/null
+++ b/feature/account/settings/impl/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="account_settings_general_title">General Settings</string>
+
+    <string name="account_settings_general_name_title">Account name</string>
+
+</resources>
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 02fa0c55826..b3af621dd18 100644
--- a/legacy/core/src/main/java/com/fsck/k9/Preferences.kt
+++ b/legacy/core/src/main/java/com/fsck/k9/Preferences.kt
@@ -222,6 +222,8 @@ class Preferences internal constructor(
             val editor = createStorageEditor()
             accountPreferenceSerializer.save(editor, storage, account)
             editor.commit()
+
+            loadAccounts()
         }

         notifyAccountsChangeListeners()

diff --git a/app-k9mail/src/test/kotlin/app/k9mail/DependencyInjectionTest.kt b/app-k9mail/src/test/kotlin/app/k9mail/DependencyInjectionTest.kt
index b13839514cc..1d7197f291e 100644
--- a/app-k9mail/src/test/kotlin/app/k9mail/DependencyInjectionTest.kt
+++ b/app-k9mail/src/test/kotlin/app/k9mail/DependencyInjectionTest.kt
@@ -12,6 +12,7 @@ import com.fsck.k9.ui.changelog.ChangelogViewModel
 import com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity
 import com.fsck.k9.ui.endtoend.AutocryptKeyTransferPresenter
 import com.fsck.k9.ui.helper.SizeFormatter
+import net.thunderbird.feature.account.api.AccountId
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.koin.core.annotation.KoinInternalApi
@@ -52,6 +53,7 @@ class DependencyInjectionTest : AutoCloseKoinTest() {
             withInstance(authStateStorage)
             withInstance(lifecycleOwner)
             withInstance(mock<WorkerParameters>())
+            withInstance(AccountId.create())
         }
     }
 }
diff --git a/app-thunderbird/src/test/kotlin/net/thunderbird/android/DependencyInjectionTest.kt b/app-thunderbird/src/test/kotlin/net/thunderbird/android/DependencyInjectionTest.kt
index 7d13578c020..41316368273 100644
--- a/app-thunderbird/src/test/kotlin/net/thunderbird/android/DependencyInjectionTest.kt
+++ b/app-thunderbird/src/test/kotlin/net/thunderbird/android/DependencyInjectionTest.kt
@@ -11,6 +11,7 @@ import com.fsck.k9.ui.changelog.ChangelogViewModel
 import com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity
 import com.fsck.k9.ui.endtoend.AutocryptKeyTransferPresenter
 import com.fsck.k9.ui.helper.SizeFormatter
+import net.thunderbird.feature.account.api.AccountId
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.koin.core.annotation.KoinInternalApi
@@ -53,6 +54,7 @@ class DependencyInjectionTest : AutoCloseKoinTest() {
             withInstance(authStateStorage)
             withInstance(lifecycleOwner)
             withInstance(mock<WorkerParameters>())
+            withInstance(AccountId.create())
         }
     }
 }
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt
index d0cfed6e330..a0aff575e16 100644
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt
+++ b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt
@@ -8,9 +8,9 @@ object FakePreferenceData {

     val textPreference = PreferenceSetting.Text(
         id = "text",
-        icon = Icons.Outlined.Delete,
-        title = "Title",
-        description = "Description",
+        icon = { Icons.Outlined.Delete },
+        title = { "Title" },
+        description = { "Description" },
         value = "Value",
     )

diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/AccountSettingsModuleKtTest.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/AccountSettingsModuleKtTest.kt
new file mode 100644
index 00000000000..e02d4d9944e
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/AccountSettingsModuleKtTest.kt
@@ -0,0 +1,53 @@
+package net.thunderbird.feature.account.settings.impl
+
+import kotlin.test.Test
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.api.profile.AccountProfile
+import net.thunderbird.feature.account.api.profile.AccountProfileRepository
+import net.thunderbird.feature.account.settings.featureAccountSettingsModule
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsViewModel
+import org.junit.runner.RunWith
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.annotation.KoinExperimentalAPI
+import org.koin.core.module.Module
+import org.koin.dsl.module
+import org.koin.test.AutoCloseKoinTest
+import org.koin.test.check.checkKoinModules
+import org.koin.test.verify.verify
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+
+@OptIn(KoinExperimentalAPI::class)
+@RunWith(RobolectricTestRunner::class)
+internal class AccountSettingsModuleKtTest : AutoCloseKoinTest() {
+
+    private val externalModule: Module = module {
+        single<AccountProfileRepository> {
+            object : AccountProfileRepository {
+                override fun getById(accountId: AccountId): Flow<AccountProfile?> = flow { null }
+
+                override suspend fun update(accountProfile: AccountProfile) = Unit
+            }
+        }
+    }
+
+    @Test
+    fun `should hava a valid di module`() {
+        featureAccountSettingsModule.verify(
+            extraTypes = listOf(
+                AccountId::class,
+                GeneralSettingsContract.State::class,
+            ),
+        )
+
+        checkKoinModules(
+            modules = listOf(externalModule, featureAccountSettingsModule),
+            appDeclaration = { androidContext(RuntimeEnvironment.getApplication()) },
+        ) {
+            withParameter<GeneralSettingsViewModel> { AccountId.create() }
+        }
+    }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/FakeAccountProfileRepository.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/FakeAccountProfileRepository.kt
new file mode 100644
index 00000000000..522a9a1bb0c
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/FakeAccountProfileRepository.kt
@@ -0,0 +1,27 @@
+package net.thunderbird.feature.account.settings.impl.domain.usecase
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.api.profile.AccountProfile
+import net.thunderbird.feature.account.api.profile.AccountProfileRepository
+
+internal class FakeAccountProfileRepository(
+    initialAccountProfile: AccountProfile? = null,
+) : AccountProfileRepository {
+
+    private val accountProfileState = MutableStateFlow<AccountProfile?>(initialAccountProfile)
+    private val accountProfile: StateFlow<AccountProfile?> = accountProfileState
+
+    override fun getById(accountId: AccountId): Flow<AccountProfile?> {
+        return accountProfile
+    }
+
+    override suspend fun update(accountProfile: AccountProfile) {
+        accountProfileState.update {
+            accountProfile
+        }
+    }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/FakeGeneralResourceProvider.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/FakeGeneralResourceProvider.kt
new file mode 100644
index 00000000000..402cd76b8ff
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/FakeGeneralResourceProvider.kt
@@ -0,0 +1,10 @@
+package net.thunderbird.feature.account.settings.impl.domain.usecase
+
+import androidx.compose.ui.graphics.vector.ImageVector
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
+
+internal class FakeGeneralResourceProvider : ResourceProvider.GeneralResourceProvider {
+    override val nameTitle: () -> String = { "Name" }
+    override val nameDescription: () -> String? = { null }
+    override val nameIcon: () -> ImageVector? = { null }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/GetGeneralPreferencesTest.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/GetGeneralPreferencesTest.kt
new file mode 100644
index 00000000000..eacaecc2dfe
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/GetGeneralPreferencesTest.kt
@@ -0,0 +1,79 @@
+package net.thunderbird.feature.account.settings.impl.domain.usecase
+
+import app.cash.turbine.test
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import assertk.assertions.isInstanceOf
+import kotlin.test.Test
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.coroutines.test.runTest
+import net.thunderbird.core.outcome.Outcome
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.api.profile.AccountProfile
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.SettingsError
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.UseCase
+
+internal class GetGeneralPreferencesTest {
+
+    @Test
+    fun `should emit preferences when account profile present`() = runTest {
+        // Arrange
+        val accountId = AccountId.create()
+        val accountProfile = AccountProfile(
+            accountId = accountId,
+            name = "Test Account",
+            color = 0xFF0000,
+        )
+        val resourceProvider = FakeGeneralResourceProvider()
+        val testSubject = createTestSubject(accountProfile)
+
+        // Act & Assert
+        testSubject(accountId).test {
+            val outcome = awaitItem()
+            assertThat(outcome).isInstanceOf(Outcome.Success::class)
+
+            val success = outcome as Outcome.Success
+            assertThat(success.data).isEqualTo(
+                persistentListOf(
+                    PreferenceSetting.Text(
+                        id = "${accountId.value}-general-name",
+                        title = resourceProvider.nameTitle,
+                        description = resourceProvider.nameDescription,
+                        icon = resourceProvider.nameIcon,
+                        value = accountProfile.name,
+                    ),
+                ),
+            )
+        }
+    }
+
+    @Test
+    fun `should emit NotFound when account profile not found`() = runTest {
+        // Arrange
+        val accountId = AccountId.create()
+        val testSubject = createTestSubject()
+
+        // Act & Assert
+        testSubject(accountId).test {
+            assertThat(awaitItem()).isEqualTo(
+                Outcome.failure(
+                    SettingsError.NotFound(
+                        message = "Account profile not found for accountId: ${accountId.value}",
+                    ),
+                ),
+            )
+        }
+    }
+
+    private fun createTestSubject(
+        accountProfile: AccountProfile? = null,
+        resourceProvider: ResourceProvider.GeneralResourceProvider = FakeGeneralResourceProvider(),
+    ): UseCase.GetGeneralPreferences {
+        return GetGeneralPreferences(
+            repository = FakeAccountProfileRepository(accountProfile),
+            resourceProvider = resourceProvider,
+        )
+    }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/UpdateGeneralPreferencesTest.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/UpdateGeneralPreferencesTest.kt
new file mode 100644
index 00000000000..00c8ed7c035
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/domain/usecase/UpdateGeneralPreferencesTest.kt
@@ -0,0 +1,72 @@
+package net.thunderbird.feature.account.settings.impl.domain.usecase
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import assertk.assertions.isInstanceOf
+import kotlin.test.Test
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.test.runTest
+import net.thunderbird.core.outcome.Outcome
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.api.profile.AccountProfile
+import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.SettingsError
+import net.thunderbird.feature.account.settings.impl.domain.entity.GeneralPreference
+import net.thunderbird.feature.account.settings.impl.domain.entity.generateId
+
+class UpdateGeneralPreferencesTest {
+
+    @Test
+    fun `should update account profile`() = runTest {
+        // Arrange
+        val accountId = AccountId.create()
+        val accountProfile = AccountProfile(
+            accountId = accountId,
+            name = "Test Account",
+            color = 0xFF0000,
+        )
+        val newName = "Updated Account Name"
+        val preference = PreferenceSetting.Text(
+            id = GeneralPreference.NAME.generateId(accountId),
+            title = { "Name" },
+            description = { "Account name" },
+            icon = { null },
+            value = newName,
+        )
+        val repository = FakeAccountProfileRepository(
+            initialAccountProfile = accountProfile,
+        )
+        val testSubject = UpdateGeneralPreferences(repository)
+
+        // Act
+        val result = testSubject(accountId, preference)
+
+        // Assert
+        assertThat(result).isInstanceOf(Outcome.Success::class)
+        assertThat(repository.getById(accountId).firstOrNull()).isEqualTo(
+            accountProfile.copy(name = newName),
+        )
+    }
+
+    @Test
+    fun `should emit NotFound when account profile not found`() = runTest {
+        // Arrange
+        val accountId = AccountId.create()
+        val preference = PreferenceSetting.Text(
+            id = GeneralPreference.NAME.generateId(accountId),
+            title = { "Name" },
+            description = { "Account name" },
+            icon = { null },
+            value = "Updated Account Name",
+        )
+        val repository = FakeAccountProfileRepository()
+        val testSubject = UpdateGeneralPreferences(repository)
+
+        // Act
+        val result = testSubject(accountId, preference)
+
+        // Assert
+        assertThat(result).isInstanceOf(Outcome.Failure::class)
+        assertThat((result as Outcome.Failure).error).isInstanceOf(SettingsError.NotFound::class)
+    }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/FakeData.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/FakeData.kt
new file mode 100644
index 00000000000..7a34c049ea4
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/FakeData.kt
@@ -0,0 +1,19 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
+import net.thunderbird.core.ui.compose.preference.api.Preference
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+
+internal object FakeData {
+
+    val preferences: ImmutableList<Preference> = persistentListOf(
+        PreferenceSetting.Text(
+            id = "test_id",
+            title = { "Title" },
+            description = { "Description" },
+            icon = { null },
+            value = "Test",
+        ),
+    )
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/FakeGeneralSettingsViewModel.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/FakeGeneralSettingsViewModel.kt
new file mode 100644
index 00000000000..ec63b489d6e
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/FakeGeneralSettingsViewModel.kt
@@ -0,0 +1,11 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import app.k9mail.core.ui.compose.testing.BaseFakeViewModel
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Effect
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Event
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.State
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.ViewModel
+
+internal class FakeGeneralSettingsViewModel(
+    initialState: State = State(),
+) : BaseFakeViewModel<State, Event, Effect>(initialState), ViewModel
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsScreenKtTest.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsScreenKtTest.kt
new file mode 100644
index 00000000000..2d5e7b887d1
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsScreenKtTest.kt
@@ -0,0 +1,58 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import app.k9mail.core.ui.compose.testing.ComposeTest
+import app.k9mail.core.ui.compose.testing.pressBack
+import app.k9mail.core.ui.compose.testing.setContentWithTheme
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import kotlin.test.Test
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Effect
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.State
+
+internal class GeneralSettingsScreenKtTest : ComposeTest() {
+
+    @Test
+    fun `should call onBack when back button is pressed`() {
+        val initialState = State()
+        val accountId = AccountId.create()
+        val viewModel = FakeGeneralSettingsViewModel(initialState)
+        var onBackCounter = 0
+
+        setContentWithTheme {
+            GeneralSettingsScreen(
+                accountId = accountId,
+                onBack = { onBackCounter++ },
+                viewModel = viewModel,
+            )
+        }
+
+        assertThat(onBackCounter).isEqualTo(0)
+
+        pressBack()
+
+        assertThat(onBackCounter).isEqualTo(1)
+    }
+
+    @Test
+    fun `should call onBack when navigate back effect received`() {
+        val initialState = State()
+        val accountId = AccountId.create()
+        val viewModel = FakeGeneralSettingsViewModel(initialState)
+        var onBackCounter = 0
+
+        setContentWithTheme {
+            GeneralSettingsScreen(
+                accountId = accountId,
+                onBack = { onBackCounter++ },
+                viewModel = viewModel,
+            )
+        }
+
+        assertThat(onBackCounter).isEqualTo(0)
+
+        viewModel.effect(Effect.NavigateBack)
+
+        assertThat(onBackCounter).isEqualTo(1)
+    }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsStateTest.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsStateTest.kt
new file mode 100644
index 00000000000..b926add26fe
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsStateTest.kt
@@ -0,0 +1,24 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import kotlin.test.Test
+import kotlinx.collections.immutable.persistentListOf
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.State
+
+internal class GeneralSettingsStateTest {
+
+    @Test
+    fun `should set default values`() {
+        // Arrange
+        val state = State()
+
+        // Assert
+        assertThat(state).isEqualTo(
+            State(
+                subtitle = null,
+                preferences = persistentListOf(),
+            ),
+        )
+    }
+}
diff --git a/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsViewModelTest.kt b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsViewModelTest.kt
new file mode 100644
index 00000000000..f5dbdad1374
--- /dev/null
+++ b/feature/account/settings/impl/src/test/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsViewModelTest.kt
@@ -0,0 +1,165 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import app.k9mail.core.ui.compose.testing.MainDispatcherRule
+import app.k9mail.core.ui.compose.testing.mvi.MviContext
+import app.k9mail.core.ui.compose.testing.mvi.MviTurbines
+import app.k9mail.core.ui.compose.testing.mvi.runMviTest
+import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import kotlin.test.Test
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.toImmutableList
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.StandardTestDispatcher
+import net.thunderbird.core.outcome.Outcome
+import net.thunderbird.core.ui.compose.preference.api.Preference
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.feature.account.api.AccountId
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.Effect
+import net.thunderbird.feature.account.settings.impl.ui.general.GeneralSettingsContract.State
+import org.junit.Rule
+
+class GeneralSettingsViewModelTest {
+
+    @get:Rule
+    val mainDispatcherRule = MainDispatcherRule(StandardTestDispatcher())
+
+    @Test
+    fun `should load general settings`() = runMviTest {
+        val accountId = AccountId.create()
+        val initialState = State(
+            subtitle = "Subtitle",
+            preferences = persistentListOf(),
+        )
+        val preferences = FakeData.preferences
+
+        generalSettingsRobot(accountId, initialState, preferences) {
+            verifyGeneralSettingsLoaded(preferences)
+        }
+    }
+
+    @Test
+    fun `should navigate back when back is pressed`() = runMviTest {
+        val accountId = AccountId.create()
+        val initialState = State(
+            subtitle = "Subtitle",
+            preferences = persistentListOf(),
+        )
+        val preferences = FakeData.preferences
+
+        generalSettingsRobot(accountId, initialState, preferences) {
+            verifyGeneralSettingsLoaded(preferences)
+            pressBack()
+            verifyBackNavigation()
+        }
+    }
+
+    @Test
+    fun `should update preference when changed`() = runMviTest {
+        val accountId = AccountId.create()
+        val initialState = State(
+            subtitle = "Subtitle",
+            preferences = persistentListOf(),
+        )
+        val preferences = FakeData.preferences
+
+        generalSettingsRobot(accountId, initialState, preferences) {
+            verifyGeneralSettingsLoaded(preferences)
+            val updatedPreference = (preferences.first() as PreferenceSetting.Text).copy(
+                title = { "Updated Title" },
+                description = { "Updated Description" },
+            )
+            updatePreference(updatedPreference)
+
+            verifyPreferenceUpdated(updatedPreference)
+        }
+    }
+}
+
+private suspend fun MviContext.generalSettingsRobot(
+    accountId: AccountId,
+    initialState: State,
+    preferences: ImmutableList<Preference>,
+    interaction: suspend GeneralSettingsRobot.() -> Unit,
+) = GeneralSettingsRobot(this, accountId, initialState, preferences).apply {
+    initialize()
+    interaction()
+}
+
+private class GeneralSettingsRobot(
+    private val mviContext: MviContext,
+    private val accountId: AccountId,
+    private val initialState: State = State(),
+    private val preferences: ImmutableList<Preference>,
+) {
+    private lateinit var preferencesState: MutableStateFlow<ImmutableList<Preference>>
+    private lateinit var turbines: MviTurbines<State, Effect>
+
+    private val viewModel: GeneralSettingsContract.ViewModel by lazy {
+        GeneralSettingsViewModel(
+            accountId = accountId,
+            getGeneralPreferences = {
+                preferencesState.map {
+                    println("Loading preferences: $it")
+                    Outcome.success(it)
+                }
+            },
+            updateGeneralPreferences = { _, preference ->
+                preferencesState.value = preferencesState.value.map { existingPreference ->
+                    if (existingPreference is PreferenceSetting<*> && existingPreference.id == preference.id) {
+                        println("Updating preference: ${preference.id}")
+                        println("Old preference: $existingPreference")
+                        println("New preference: $preference")
+                        preference
+                    } else {
+                        existingPreference
+                    }
+                }.toImmutableList()
+                Outcome.success(Unit)
+            },
+            initialState = initialState,
+        )
+    }
+
+    suspend fun initialize() {
+        preferencesState = MutableStateFlow(preferences)
+
+        turbines = mviContext.turbinesWithInitialStateCheck(
+            initialState = initialState,
+            viewModel = viewModel,
+        )
+    }
+
+    suspend fun verifyGeneralSettingsLoaded(preferences: ImmutableList<Preference>) {
+        assertThat(turbines.awaitStateItem()).isEqualTo(
+            initialState.copy(
+                preferences = preferences,
+            ),
+        )
+    }
+
+    fun pressBack() {
+        viewModel.event(GeneralSettingsContract.Event.OnBackPressed)
+    }
+
+    suspend fun verifyBackNavigation() {
+        assertThat(turbines.awaitEffectItem()).isEqualTo(
+            Effect.NavigateBack,
+        )
+    }
+
+    fun updatePreference(preference: PreferenceSetting<*>) {
+        viewModel.event(GeneralSettingsContract.Event.OnPreferenceSettingChange(preference))
+    }
+
+    suspend fun verifyPreferenceUpdated(preference: PreferenceSetting<*>) {
+        val updatedPreference = turbines.awaitStateItem().preferences
+            .filterIsInstance<PreferenceSetting<*>>()
+            .find { it.id == preference.id }
+
+        assertThat(updatedPreference).isEqualTo(preference)
+    }
+}
diff --git a/legacy/core/src/test/java/com/fsck/k9/PreferencesTest.kt b/legacy/core/src/test/java/com/fsck/k9/PreferencesTest.kt
index 4243854bded..8a719081720 100644
--- a/legacy/core/src/test/java/com/fsck/k9/PreferencesTest.kt
+++ b/legacy/core/src/test/java/com/fsck/k9/PreferencesTest.kt
@@ -1,6 +1,8 @@
 package com.fsck.k9

+import app.k9mail.legacy.account.LegacyAccount
 import assertk.assertThat
+import assertk.assertions.isEqualTo
 import assertk.assertions.isSameInstanceAs
 import com.fsck.k9.mail.AuthType
 import com.fsck.k9.mail.ConnectionSecurity
@@ -33,8 +35,8 @@ class PreferencesTest {

     @Test
     fun `reloading accounts should return same Account instance`() {
-        createAccount(ACCOUNT_UUID_ONE)
-        createAccount(ACCOUNT_UUID_TWO)
+        createAndSaveAccount(ACCOUNT_UUID_ONE)
+        createAndSaveAccount(ACCOUNT_UUID_TWO)
         val firstAccountOne = preferences.getAccount(ACCOUNT_UUID_ONE)

         preferences.loadAccounts()
@@ -43,7 +45,32 @@ class PreferencesTest {
         assertThat(firstAccountTwo).isSameInstanceAs(firstAccountOne)
     }

-    private fun createAccount(accountUuid: String) {
+    @Test
+    fun `saving accounts should return updated Account instance`() {
+        val account = createAccount(ACCOUNT_UUID_ONE)
+        preferences.saveAccount(account)
+
+        val updatedAccount = createAccount(ACCOUNT_UUID_ONE).apply {
+            name = "New name"
+        }
+
+        preferences.saveAccount(updatedAccount)
+
+        val currentAccountOne = preferences.getAccount(ACCOUNT_UUID_ONE)!!
+        assertThat(currentAccountOne.name).isEqualTo("New name")
+    }
+
+    private fun createAccount(accountId: String): LegacyAccount {
+        return LegacyAccount(
+            uuid = accountId,
+            isSensitiveDebugLoggingEnabled = { false },
+        ).apply {
+            incomingServerSettings = SERVER_SETTINGS
+            outgoingServerSettings = SERVER_SETTINGS
+        }
+    }
+
+    private fun createAndSaveAccount(accountUuid: String) {
         val account = preferences.newAccount(accountUuid).apply {
             // To be able to persist `Account` we need to set server settings
             incomingServerSettings = SERVER_SETTINGS


Move logging functionality to core.android.logging module

- Fixes #8975

Move com.fsck.k9.logging  to core module

Move com.fsck.k9.logging  to core module
The `com.fsck.k9.logging` functionality in the `:legacy:core` module should be moved to `:core:android:logging` and the `net.thunderbird.core.android.logging` package.

Alternatively it could be moved to `:core:android:common` and the `net.thunderbird.core.android.logging` package.

Part of #7852

./gradlew :legacy:ui:legacy:testDebugUnitTest --tests "com.fsck.k9.ui.settings.general.GeneralSettingsViewModelTest" :core:android:logging:testDebugUnitTest --tests "net.thunderbird.core.android.logging.LogcatLogFileWriterTest"

diff --git a/core/android/logging/build.gradle.kts b/core/android/logging/build.gradle.kts
new file mode 100644
index 00000000000..86a28e09ff0
--- /dev/null
+++ b/core/android/logging/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+    id(ThunderbirdPlugins.Library.android)
+}
+
+android {
+    namespace = "net.thunderbird.core.android.logging"
+}
+
+dependencies {
+    implementation(libs.timber)
+    implementation(libs.commons.io)
+}
diff --git a/legacy/core/src/main/java/com/fsck/k9/logging/KoinModule.kt b/core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/KoinModule.kt
similarity index 82%
rename from legacy/core/src/main/java/com/fsck/k9/logging/KoinModule.kt
rename to core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/KoinModule.kt
index 4f0cf604802..0892cddd6d1 100644
--- a/legacy/core/src/main/java/com/fsck/k9/logging/KoinModule.kt
+++ b/core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/KoinModule.kt
@@ -1,4 +1,4 @@
-package com.fsck.k9.logging
+package net.thunderbird.core.android.logging

 import org.koin.dsl.module

diff --git a/legacy/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt b/core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/LogFileWriter.kt
similarity index 96%
rename from legacy/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt
rename to core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/LogFileWriter.kt
index 0797c006b71..e541bac9c1d 100644
--- a/legacy/core/src/main/java/com/fsck/k9/logging/LogFileWriter.kt
+++ b/core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/LogFileWriter.kt
@@ -1,4 +1,4 @@
-package com.fsck.k9.logging
+package net.thunderbird.core.android.logging

 import android.content.ContentResolver
 import android.net.Uri
diff --git a/legacy/core/src/main/java/com/fsck/k9/logging/ProcessExecutor.kt b/core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/ProcessExecutor.kt
similarity index 87%
rename from legacy/core/src/main/java/com/fsck/k9/logging/ProcessExecutor.kt
rename to core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/ProcessExecutor.kt
index cfa2ff653fc..dc437dd61df 100644
--- a/legacy/core/src/main/java/com/fsck/k9/logging/ProcessExecutor.kt
+++ b/core/android/logging/src/main/kotlin/net/thunderbird/core/android/logging/ProcessExecutor.kt
@@ -1,4 +1,4 @@
-package com.fsck.k9.logging
+package net.thunderbird.core.android.logging

 import java.io.InputStream

diff --git a/legacy/core/build.gradle.kts b/legacy/core/build.gradle.kts
index ae7a0dde3ff..0fa7c8e82c9 100644
--- a/legacy/core/build.gradle.kts
+++ b/legacy/core/build.gradle.kts
@@ -8,6 +8,7 @@ dependencies {
     api(projects.backend.api)
     api(projects.library.htmlCleaner)
     api(projects.core.android.common)
+    api(projects.core.android.logging)
     api(projects.core.android.network)
     api(projects.core.mail.folder.api)

diff --git a/legacy/core/src/main/java/com/fsck/k9/CoreKoinModules.kt b/legacy/core/src/main/java/com/fsck/k9/CoreKoinModules.kt
index 84c58690971..f70ab1cd44b 100644
--- a/legacy/core/src/main/java/com/fsck/k9/CoreKoinModules.kt
+++ b/legacy/core/src/main/java/com/fsck/k9/CoreKoinModules.kt
@@ -6,7 +6,6 @@ import com.fsck.k9.controller.push.controllerPushModule
 import com.fsck.k9.crypto.openPgpModule
 import com.fsck.k9.helper.helperModule
 import com.fsck.k9.job.jobModule
-import com.fsck.k9.logging.loggingModule
 import com.fsck.k9.mailstore.mailStoreModule
 import com.fsck.k9.message.extractors.extractorModule
 import com.fsck.k9.message.html.htmlModule
@@ -14,6 +13,7 @@ import com.fsck.k9.message.quote.quoteModule
 import com.fsck.k9.notification.coreNotificationModule
 import com.fsck.k9.power.powerModule
 import com.fsck.k9.preferences.preferencesModule
+import net.thunderbird.core.android.logging.loggingModule
 import net.thunderbird.core.android.network.coreAndroidNetworkModule

 val coreModules = listOf(
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModel.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModel.kt
index 893d5de601f..20ea2d1756a 100644
--- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModel.kt
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModel.kt
@@ -3,12 +3,12 @@ package com.fsck.k9.ui.settings.general
 import android.net.Uri
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
-import com.fsck.k9.logging.LogFileWriter
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.launch
+import net.thunderbird.core.android.logging.LogFileWriter
 import timber.log.Timber

 class GeneralSettingsViewModel(private val logFileWriter: LogFileWriter) : ViewModel() {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 0d10145af31..0b39e2ed3e6 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -219,3 +219,4 @@ check(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
         https://developer.android.com/build/jdks#jdk-config-in-studio
     """.trimIndent()
 }
+include(":core:android:logging")

diff --git a/legacy/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt b/core/android/logging/src/test/kotlin/net/thunderbird/core/android/logging/LogcatLogFileWriterTest.kt
similarity index 98%
rename from legacy/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt
rename to core/android/logging/src/test/kotlin/net/thunderbird/core/android/logging/LogcatLogFileWriterTest.kt
index 9783531f010..7cc43bf5c7f 100644
--- a/legacy/core/src/test/java/com/fsck/k9/logging/LogcatLogFileWriterTest.kt
+++ b/core/android/logging/src/test/kotlin/net/thunderbird/core/android/logging/LogcatLogFileWriterTest.kt
@@ -1,4 +1,4 @@
-package com.fsck.k9.logging
+package net.thunderbird.core.android.logging

 import android.content.ContentResolver
 import android.net.Uri
diff --git a/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModelTest.kt b/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModelTest.kt
index ef1875cbe18..3a27a1b2366 100644
--- a/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModelTest.kt
+++ b/legacy/ui/legacy/src/test/java/com/fsck/k9/ui/settings/general/GeneralSettingsViewModelTest.kt
@@ -5,7 +5,6 @@ import app.cash.turbine.test
 import assertk.assertThat
 import assertk.assertions.isEmpty
 import assertk.assertions.isEqualTo
-import com.fsck.k9.logging.LogFileWriter
 import java.io.IOException
 import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.Dispatchers
@@ -19,6 +18,7 @@ import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.resetMain
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.test.setMain
+import net.thunderbird.core.android.logging.LogFileWriter
 import org.junit.After
 import org.junit.Before
 import org.junit.Test


Fixes Unified inbox doesn't show up

- Fixes #8757
- Added tests to cover `UnifiedInboxConfigurator`'s functionality

Unified inbox doesn't show up

Unified inbox doesn't show up
- [ ] ### Checklist

- [x] I have used the search function to see if someone else has already submitted the same bug report.
- [x] I will describe the problem with as much detail as possible.

### App

Thunderbird for Android

### App version

8.2

### Where did you get the app from?

Google Play

### Android version

15

### Device model

OnePlus 11 5G

### Steps to reproduce

1. I uninstalled the Thunderbird app on my phone since it was not getting new emails, while my Gmail and Outlook programs were getting it.
2. So, I re-installed Thunderbird from Google Play Store.
3. I opened Thunderbird after installation and added the first email account from Gmail. I did not see the Unified box, but I thought maybe it's because there's only one email account added. But the gmail account was receiving emails.
4. So I proceeded to add a second email account from Gmail and to my surprise, I still did not see a Unified box.
5. I added a third email account from Outlook and still no Unified box showed up, even though each individual account was getting and showing it's emails from the email server.
6. I then closed the Thunderbird app and re- opened it.
7. To my shock and surprise the Unified box was now showing after some frustrating steps.

In my view, the Unified box should always be there whether there's one email account or more than one. OR you could not show the Unified box if only one account is added, but automatically show it as soon as the second account is added. The first option of always showing Unified box seems more intuitive to me and a better option for your app users.

### Expected behavior

The Unified box should have been there as soon as an email account was added.

### Actual behavior

No Unified box showed up after adding the first email account and after adding the next two accounts.

### Logs

_No response_

I just experienced something similar. I installed Thunderbird In my Samsung tablet running android 14. I added 3 mailboxes, no unified inbox. I did a force close, still no unified inbox. I had to force close a total of 5 times before the unified inbox showed up.
This is a bug.

Expected behavior: When setting up a second account, the inbox should be enabled automatically. However, adding a third account should preserve existing settings, specifically respecting users who have disabled unified inbox configuration.

./gradlew :legacy:core:testDebugUnitTest --tests "com.fsck.k9.UnifiedInboxConfiguratorTest"

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 ac2a2e65fce..11b308798fd 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
@@ -10,7 +10,7 @@ class UnifiedInboxConfigurator(
     private val accountManager: AccountManager,
 ) {
     fun configureUnifiedInbox() {
-        if (accountManager.getAccounts().size > 1) {
+        if (accountManager.getAccounts().size == 2) {
             K9.isShowUnifiedInbox = true
             K9.saveSettingsAsync()
         }

diff --git a/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt b/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt
new file mode 100644
index 00000000000..064bbd13e0f
--- /dev/null
+++ b/legacy/core/src/test/java/com/fsck/k9/UnifiedInboxConfiguratorTest.kt
@@ -0,0 +1,82 @@
+package com.fsck.k9
+
+import app.k9mail.legacy.account.AccountManager
+import com.fsck.k9.preferences.RealGeneralSettingsManager
+import com.fsck.k9.preferences.UnifiedInboxConfigurator
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.koin.core.context.GlobalContext.startKoin
+import org.koin.core.context.GlobalContext.stopKoin
+import org.koin.dsl.module
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+
+@RunWith(MockitoJUnitRunner::class)
+class UnifiedInboxConfiguratorTest {
+
+    private lateinit var accountManager: AccountManager
+    private lateinit var configurator: UnifiedInboxConfigurator
+
+    @Before
+    fun setUp() {
+        accountManager = mock(AccountManager::class.java)
+        configurator = UnifiedInboxConfigurator(accountManager)
+
+        // Start Koin with a minimal module
+        startKoin {
+            modules(
+                module {
+                    single { mock(RealGeneralSettingsManager::class.java) }
+                },
+            )
+        }
+
+        // Reset K9 settings to avoid state leakage across tests
+        K9.isShowUnifiedInbox = false
+    }
+
+    @After
+    fun tearDown() {
+        stopKoin()
+    }
+
+    @Test
+    fun `configureUnifiedInbox should enable unified inbox when there are exactly two accounts`() {
+        // Given
+        `when`(accountManager.getAccounts()).thenReturn(listOf(mock(), mock()))
+
+        // When
+        configurator.configureUnifiedInbox()
+
+        // Then
+        assertTrue(K9.isShowUnifiedInbox)
+    }
+
+    @Test
+    fun `configureUnifiedInbox should not enable unified inbox when there are less than two accounts`() {
+        // Given
+        `when`(accountManager.getAccounts()).thenReturn(listOf(mock()))
+
+        // When
+        configurator.configureUnifiedInbox()
+
+        // Then
+        assertTrue(!K9.isShowUnifiedInbox)
+    }
+
+    @Test
+    fun `configureUnifiedInbox should not enable unified inbox when there are more than two accounts`() {
+        // Given
+        `when`(accountManager.getAccounts()).thenReturn(listOf(mock(), mock(), mock()))
+
+        // When
+        configurator.configureUnifiedInbox()
+
+        // Then
+        assertTrue(!K9.isShowUnifiedInbox)
+    }
+}