Repository: thunderbird/thunderbird-android (language: kotlin)

Issue:
Account Settings - Account profile view
The general settings page should include a preview of the account avatar.


Discussion hints:
nan

Pull request: Add account settings profile view
Resolves #8926

<img src="https://github.com/user-attachments/assets/7fc979ed-72f8-4124-aa20-16b36e59b232" width="250em" />

Depends on #9027 



Test command: ./gradlew :feature:account:settings:impl:testDebugUnitTest --tests "net.thunderbird.feature.account.settings.impl.domain.usecase.UpdateGeneralPreferencesTest" --tests "net.thunderbird.feature.account.settings.impl.domain.usecase.GetGeneralPreferencesTest"

Fix patch:
diff --git a/app-common/build.gradle.kts b/app-common/build.gradle.kts
index 9338077e3eb..43746959203 100644
--- a/app-common/build.gradle.kts
+++ b/app-common/build.gradle.kts
@@ -15,6 +15,10 @@ dependencies {
     implementation(projects.legacy.account)
 
     implementation(projects.core.featureflags)
+    implementation(projects.core.ui.legacy.theme2.common)
 
+    implementation(projects.feature.account.setup)
     implementation(projects.feature.migration.provider)
+
+    implementation(projects.mail.protocols.imap)
 }
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/AppCommonModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/AppCommonModule.kt
index 01e151d74ae..a6a5259401d 100644
--- a/app-common/src/main/kotlin/net/thunderbird/app/common/AppCommonModule.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/AppCommonModule.kt
@@ -1,36 +1,11 @@
 package net.thunderbird.app.common
 
-import app.k9mail.legacy.account.AccountDefaultsProvider
-import app.k9mail.legacy.account.LegacyAccountWrapperManager
-import net.thunderbird.app.common.account.CommonAccountDefaultsProvider
-import net.thunderbird.app.common.account.data.CommonAccountProfileLocalDataSource
-import net.thunderbird.app.common.account.data.CommonLegacyAccountWrapperManager
-import net.thunderbird.feature.account.core.AccountCoreExternalContract.AccountProfileLocalDataSource
-import net.thunderbird.feature.account.core.featureAccountCoreModule
+import net.thunderbird.app.common.account.appCommonAccountModule
 import org.koin.core.module.Module
 import org.koin.dsl.module
 
 val appCommonModule: Module = module {
     includes(
-        featureAccountCoreModule,
+        appCommonAccountModule,
     )
-
-    single<LegacyAccountWrapperManager> {
-        CommonLegacyAccountWrapperManager(
-            accountManager = get(),
-        )
-    }
-
-    single<AccountProfileLocalDataSource> {
-        CommonAccountProfileLocalDataSource(
-            accountManager = get(),
-        )
-    }
-
-    single<AccountDefaultsProvider> {
-        CommonAccountDefaultsProvider(
-            resourceProvider = get(),
-            featureFlagProvider = get(),
-        )
-    }
 }
diff --git a/legacy/common/src/main/java/com/fsck/k9/account/AccountColorPicker.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt
similarity index 91%
rename from legacy/common/src/main/java/com/fsck/k9/account/AccountColorPicker.kt
rename to app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt
index 777650879e9..47f9c84ff23 100644
--- a/legacy/common/src/main/java/com/fsck/k9/account/AccountColorPicker.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountColorPicker.kt
@@ -1,8 +1,8 @@
-package com.fsck.k9.account
+package net.thunderbird.app.common.account
 
 import android.content.res.Resources
+import app.k9mail.core.ui.legacy.theme2.common.R
 import app.k9mail.legacy.account.AccountManager
-import com.fsck.k9.core.R
 
 class AccountColorPicker(
     private val accountManager: AccountManager,
diff --git a/legacy/common/src/main/java/com/fsck/k9/account/AccountCreator.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountCreator.kt
similarity index 93%
rename from legacy/common/src/main/java/com/fsck/k9/account/AccountCreator.kt
rename to app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountCreator.kt
index faab18717c4..adddacde92f 100644
--- a/legacy/common/src/main/java/com/fsck/k9/account/AccountCreator.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AccountCreator.kt
@@ -1,4 +1,4 @@
-package com.fsck.k9.account
+package net.thunderbird.app.common.account
 
 import android.content.Context
 import app.k9mail.core.common.mail.Protocols
@@ -7,9 +7,11 @@ import app.k9mail.feature.account.common.domain.entity.SpecialFolderOption
 import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings
 import app.k9mail.feature.account.setup.AccountSetupExternalContract
 import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult
+import app.k9mail.legacy.account.LegacyAccount
 import app.k9mail.legacy.account.SpecialFolderSelection
 import com.fsck.k9.Core
 import com.fsck.k9.Preferences
+import com.fsck.k9.account.DeletePolicyProvider
 import com.fsck.k9.controller.MessagingController
 import com.fsck.k9.logging.Timber
 import com.fsck.k9.mail.ServerSettings
@@ -18,13 +20,11 @@ import com.fsck.k9.mail.store.imap.ImapStoreSettings.createExtra
 import com.fsck.k9.mail.store.imap.ImapStoreSettings.isSendClientInfo
 import com.fsck.k9.mail.store.imap.ImapStoreSettings.isUseCompression
 import com.fsck.k9.mail.store.imap.ImapStoreSettings.pathPrefix
-import com.fsck.k9.mailstore.SpecialFolderUpdater
 import com.fsck.k9.mailstore.SpecialLocalFoldersCreator
 import com.fsck.k9.preferences.UnifiedInboxConfigurator
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
-import app.k9mail.legacy.account.LegacyAccount as K9Account
 
 // TODO Move to feature/account/setup
 class AccountCreator(
@@ -99,9 +99,9 @@ class AccountCreator(
      * Set special folders by name.
      *
      * Since the folder list hasn't been synced yet, we don't have database IDs for the folders. So we use the same
-     * mechanism that is used when importing settings. See [SpecialFolderUpdater] for details.
+     * mechanism that is used when importing settings. See [com.fsck.k9.mailstore.SpecialFolderUpdater] for details.
      */
-    private fun K9Account.setSpecialFolders(specialFolders: SpecialFolderSettings) {
+    private fun LegacyAccount.setSpecialFolders(specialFolders: SpecialFolderSettings) {
         importedArchiveFolder = specialFolders.archiveSpecialFolderOption.toFolderServerId()
         archiveFolderSelection = specialFolders.archiveSpecialFolderOption.toFolderSelection()
 
@@ -141,7 +141,7 @@ class AccountCreator(
     }
 }
 
-private fun K9Account.setIncomingServerSettings(serverSettings: ServerSettings) {
+private fun LegacyAccount.setIncomingServerSettings(serverSettings: ServerSettings) {
     if (serverSettings.type == Protocols.IMAP) {
         useCompression = serverSettings.isUseCompression
         isSendClientInfoEnabled = serverSettings.isSendClientInfo
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
new file mode 100644
index 00000000000..2d0044b63e8
--- /dev/null
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/account/AppCommonAccountModule.kt
@@ -0,0 +1,55 @@
+package net.thunderbird.app.common.account
+
+import app.k9mail.feature.account.setup.AccountSetupExternalContract
+import app.k9mail.legacy.account.AccountDefaultsProvider
+import app.k9mail.legacy.account.LegacyAccountWrapperManager
+import net.thunderbird.app.common.account.data.CommonAccountProfileLocalDataSource
+import net.thunderbird.app.common.account.data.CommonLegacyAccountWrapperManager
+import net.thunderbird.feature.account.core.AccountCoreExternalContract.AccountProfileLocalDataSource
+import net.thunderbird.feature.account.core.featureAccountCoreModule
+import org.koin.android.ext.koin.androidApplication
+import org.koin.dsl.module
+
+internal val appCommonAccountModule = module {
+    includes(
+        featureAccountCoreModule,
+    )
+
+    single<LegacyAccountWrapperManager> {
+        CommonLegacyAccountWrapperManager(
+            accountManager = get(),
+        )
+    }
+
+    single<AccountProfileLocalDataSource> {
+        CommonAccountProfileLocalDataSource(
+            accountManager = get(),
+        )
+    }
+
+    single<AccountDefaultsProvider> {
+        CommonAccountDefaultsProvider(
+            resourceProvider = get(),
+            featureFlagProvider = get(),
+        )
+    }
+
+    factory {
+        AccountColorPicker(
+            accountManager = get(),
+            resources = get(),
+        )
+    }
+
+    factory<AccountSetupExternalContract.AccountCreator> {
+        AccountCreator(
+            accountColorPicker = get(),
+            localFoldersCreator = get(),
+            preferences = get(),
+            context = androidApplication(),
+            deletePolicyProvider = get(),
+            messagingController = get(),
+            unifiedInboxConfigurator = get(),
+        )
+    }
+}
diff --git a/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardElevatedPreview.kt b/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardElevatedPreview.kt
new file mode 100644
index 00000000000..cc918ad270e
--- /dev/null
+++ b/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardElevatedPreview.kt
@@ -0,0 +1,22 @@
+package app.k9mail.core.ui.compose.designsystem.atom.card
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
+import app.k9mail.core.ui.compose.theme2.MainTheme
+
+@Composable
+@Preview(showBackground = true)
+internal fun CardElevatedPreview() {
+    PreviewWithThemes {
+        CardElevated {
+            Box(modifier = Modifier.padding(MainTheme.spacings.double)) {
+                TextBodyMedium("Text in card")
+            }
+        }
+    }
+}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardElevated.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardElevated.kt
new file mode 100644
index 00000000000..f622631c00e
--- /dev/null
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardElevated.kt
@@ -0,0 +1,21 @@
+package app.k9mail.core.ui.compose.designsystem.atom.card
+
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.material3.CardDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.material3.Card as Material3Card
+
+@Composable
+fun CardElevated(
+    modifier: Modifier = Modifier,
+    content: @Composable ColumnScope.() -> Unit,
+) {
+    Material3Card(
+        modifier = modifier,
+        shape = CardDefaults.elevatedShape,
+        colors = CardDefaults.elevatedCardColors(),
+        elevation = CardDefaults.elevatedCardElevation(),
+        content = content,
+    )
+}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt
index 70802a44547..0f8bf5a72f3 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/card/CardFilled.kt
@@ -1,6 +1,7 @@
 package app.k9mail.core.ui.compose.designsystem.atom.card
 
 import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.material3.CardDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.material3.Card as Material3Card
@@ -12,6 +13,9 @@ fun CardFilled(
 ) {
     Material3Card(
         modifier = modifier,
+        shape = CardDefaults.shape,
+        colors = CardDefaults.cardColors(),
+        elevation = CardDefaults.cardElevation(),
         content = content,
     )
 }
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/common/ColoreViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/common/ColoreViewPreview.kt
new file mode 100644
index 00000000000..deffd853f4a
--- /dev/null
+++ b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/common/ColoreViewPreview.kt
@@ -0,0 +1,28 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.common
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+
+@Composable
+@Preview(showBackground = true)
+internal fun ColorViewPreview() {
+    PreviewWithThemes {
+        ColorView(
+            color = 0xFFFF0000.toInt(),
+            onClick = null,
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun ColorViewWithSelectionPreview() {
+    PreviewWithThemes {
+        ColorView(
+            color = 0xFFFF0000.toInt(),
+            onClick = null,
+            isSelected = true,
+        )
+    }
+}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogColorViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogColorViewPreview.kt
new file mode 100644
index 00000000000..0370259496e
--- /dev/null
+++ b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogColorViewPreview.kt
@@ -0,0 +1,19 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.dialog
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
+import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
+
+@Composable
+@Preview(showBackground = true)
+internal fun PreferenceDialogColorViewPreview() {
+    PreviewWithTheme {
+        PreferenceDialogColorView(
+            preference = FakePreferenceData.colorPreference,
+            onConfirmClick = {},
+            onDismissClick = {},
+            onDismissRequest = {},
+        )
+    }
+}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorViewPreview.kt
new file mode 100644
index 00000000000..ee50ff30f31
--- /dev/null
+++ b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorViewPreview.kt
@@ -0,0 +1,17 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.list
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
+
+@Composable
+@Preview(showBackground = true)
+internal fun PreferenceItemColorViewPreview() {
+    PreviewWithThemes {
+        PreferenceItemColorView(
+            preference = FakePreferenceData.colorPreference,
+            onClick = {},
+        )
+    }
+}
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 1b818674060..bbf9038aba5 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
@@ -1,6 +1,8 @@
 package net.thunderbird.core.ui.compose.preference.api
 
 import android.os.Parcelable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import kotlinx.parcelize.IgnoredOnParcel
 import kotlinx.parcelize.Parcelize
@@ -8,13 +10,14 @@ import kotlinx.parcelize.Parcelize
 /**
  * A preference that can be displayed in a preference screen.
  */
-sealed interface Preference : Parcelable
+sealed interface Preference : Parcelable {
+    val id: String
+}
 
 /**
  * A preference that holds a value of type [T].
  */
 sealed interface PreferenceSetting<T> : Preference {
-    val id: String
     val value: T
     val requiresEditView: Boolean
 
@@ -29,4 +32,29 @@ sealed interface PreferenceSetting<T> : Preference {
         @IgnoredOnParcel
         override val requiresEditView: Boolean = true
     }
+
+    @Parcelize
+    data class Color(
+        override val id: String,
+        val title: () -> String,
+        val description: () -> String? = { null },
+        val icon: () -> ImageVector? = { null },
+        override val value: Int,
+        val colors: List<Int>,
+    ) : PreferenceSetting<Int> {
+        @IgnoredOnParcel
+        override val requiresEditView: Boolean = true
+    }
+}
+
+/**
+ * A preference that does not hold a value. It is used to display a section, a divider or custom UI.
+ */
+sealed interface PreferenceDisplay : Preference {
+
+    @Parcelize
+    data class Custom(
+        override val id: String,
+        val customUi: @Composable (Modifier) -> Unit,
+    ) : PreferenceDisplay
 }
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/common/ColorView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/common/ColorView.kt
new file mode 100644
index 00000000000..2789cee903b
--- /dev/null
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/common/ColorView.kt
@@ -0,0 +1,46 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.common
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
+import app.k9mail.core.ui.compose.designsystem.atom.Surface
+import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
+import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
+import app.k9mail.core.ui.compose.theme2.MainTheme
+
+@Composable
+internal fun ColorView(
+    color: Int,
+    onClick: ((Int) -> Unit)?,
+    modifier: Modifier = Modifier,
+    isSelected: Boolean = false,
+    size: Dp = MainTheme.sizes.icon,
+) {
+    Surface(
+        color = Color(color),
+        modifier = modifier
+            .size(size)
+            .clip(CircleShape)
+            .let {
+                if (onClick != null) {
+                    it.clickable(onClick = { onClick(color) })
+                } else {
+                    it
+                }
+            },
+    ) {
+        if (isSelected) {
+            Icon(
+                tint = MainTheme.colors.onSecondary,
+                imageVector = Icons.Outlined.Check,
+                modifier = Modifier.padding(MainTheme.spacings.default),
+            )
+        }
+    }
+}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt
index 26727514337..b032844b688 100644
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt
@@ -27,5 +27,15 @@ internal fun PreferenceDialog(
                 modifier = modifier,
             )
         }
+
+        is PreferenceSetting.Color -> {
+            PreferenceDialogColorView(
+                preference = preference,
+                onConfirmClick = onConfirmClick,
+                onDismissClick = onDismissClick,
+                onDismissRequest = onDismissRequest,
+                modifier = modifier,
+            )
+        }
     }
 }
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogColorView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogColorView.kt
new file mode 100644
index 00000000000..dec5e763a4a
--- /dev/null
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialogColorView.kt
@@ -0,0 +1,62 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.dialog
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
+import app.k9mail.core.ui.compose.theme2.MainTheme
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.core.ui.compose.preference.ui.components.common.ColorView
+
+@Composable
+internal fun PreferenceDialogColorView(
+    preference: PreferenceSetting.Color,
+    onConfirmClick: (PreferenceSetting<*>) -> Unit,
+    onDismissClick: () -> Unit,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    val currentColor = rememberSaveable { mutableIntStateOf(preference.value) }
+    val gridState = rememberLazyGridState()
+
+    PreferenceDialogLayout(
+        title = preference.title(),
+        icon = preference.icon(),
+        onConfirmClick = { onConfirmClick(preference.copy(value = currentColor.intValue)) },
+        onDismissClick = onDismissClick,
+        onDismissRequest = onDismissRequest,
+        modifier = modifier,
+    ) {
+        preference.description()?.let {
+            TextBodyMedium(text = it)
+
+            Spacer(modifier = Modifier.height(MainTheme.spacings.double))
+        }
+        LazyVerticalGrid(
+            state = gridState,
+            columns = GridCells.Adaptive(minSize = MainTheme.sizes.iconAvatar),
+            verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
+            horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
+        ) {
+            items(preference.colors) { color ->
+                ColorView(
+                    color = color,
+                    onClick = { newColor ->
+                        currentColor.intValue = newColor
+                    },
+                    isSelected = color == currentColor.intValue,
+                    modifier = Modifier.size(MainTheme.sizes.iconAvatar),
+                )
+            }
+        }
+    }
+}
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 7866e278866..1ebc8187052 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
@@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.height
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
 import app.k9mail.core.ui.compose.designsystem.molecule.input.TextInput
@@ -20,13 +20,13 @@ internal fun PreferenceDialogTextView(
     onDismissRequest: () -> Unit,
     modifier: Modifier = Modifier,
 ) {
-    val currentPreference = remember { mutableStateOf(preference) }
+    val currentText = rememberSaveable { mutableStateOf(preference.value) }
 
     PreferenceDialogLayout(
         title = preference.title(),
         icon = preference.icon(),
         onConfirmClick = {
-            onConfirmClick(currentPreference.value)
+            onConfirmClick(preference.copy(value = currentText.value))
         },
         onDismissClick = onDismissClick,
         onDismissRequest = onDismissRequest,
@@ -39,9 +39,11 @@ internal fun PreferenceDialogTextView(
         }
 
         TextInput(
-            text = currentPreference.value.value,
+            text = currentText.value,
             contentPadding = PaddingValues(),
-            onTextChange = { currentPreference.value = currentPreference.value.copy(value = it) },
+            onTextChange = {
+                currentText.value = it
+            },
         )
     }
 }
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt
index ec6be2b48ff..18ba30e8689 100644
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt
@@ -3,6 +3,7 @@ package net.thunderbird.core.ui.compose.preference.ui.components.list
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import net.thunderbird.core.ui.compose.preference.api.Preference
+import net.thunderbird.core.ui.compose.preference.api.PreferenceDisplay
 import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
 
 @Composable
@@ -12,6 +13,7 @@ internal fun PreferenceItem(
     modifier: Modifier = Modifier,
 ) {
     when (preference) {
+        // PreferenceSetting
         is PreferenceSetting.Text -> {
             PreferenceItemTextView(
                 preference = preference,
@@ -19,5 +21,21 @@ internal fun PreferenceItem(
                 modifier = modifier,
             )
         }
+
+        is PreferenceSetting.Color -> {
+            PreferenceItemColorView(
+                preference = preference,
+                onClick = onClick,
+                modifier = modifier,
+            )
+        }
+
+        // PreferenceDisplay
+        is PreferenceDisplay.Custom -> {
+            PreferenceItemCustomView(
+                preference = preference,
+                modifier = modifier,
+            )
+        }
     }
 }
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorView.kt
new file mode 100644
index 00000000000..a747d18b1af
--- /dev/null
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorView.kt
@@ -0,0 +1,44 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.list
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
+import app.k9mail.core.ui.compose.theme2.MainTheme
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+import net.thunderbird.core.ui.compose.preference.ui.components.common.ColorView
+
+@Composable
+internal fun PreferenceItemColorView(
+    preference: PreferenceSetting.Color,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    PreferenceItemLayout(
+        onClick = onClick,
+        icon = preference.icon(),
+        modifier = modifier,
+    ) {
+        Row(
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            Column(
+                Modifier.weight(1f),
+            ) {
+                TextTitleMedium(text = preference.title())
+                preference.description()?.let {
+                    TextBodyMedium(text = it)
+                }
+            }
+            ColorView(
+                color = preference.value,
+                onClick = null,
+                modifier = Modifier.padding(start = MainTheme.spacings.default),
+            )
+        }
+    }
+}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemCustomView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemCustomView.kt
new file mode 100644
index 00000000000..bc1227cd7b2
--- /dev/null
+++ b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemCustomView.kt
@@ -0,0 +1,13 @@
+package net.thunderbird.core.ui.compose.preference.ui.components.list
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import net.thunderbird.core.ui.compose.preference.api.PreferenceDisplay
+
+@Composable
+internal fun PreferenceItemCustomView(
+    preference: PreferenceDisplay.Custom,
+    modifier: Modifier = Modifier,
+) {
+    preference.customUi(modifier)
+}
diff --git a/core/ui/legacy/theme2/common/src/main/res/values/account_colors.xml b/core/ui/legacy/theme2/common/src/main/res/values/account_colors.xml
new file mode 100644
index 00000000000..976b4ce3eb7
--- /dev/null
+++ b/core/ui/legacy/theme2/common/src/main/res/values/account_colors.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer-array name="account_colors">
+        <item>@color/material_amber_600</item>
+        <item>@color/material_orange_600</item>
+        <item>@color/material_deep_orange_600</item>
+        <item>@color/material_red_600</item>
+
+        <item>@color/material_lime_600</item>
+        <item>@color/material_light_green_600</item>
+        <item>@color/material_green_700</item>
+        <item>@color/material_teal_600</item>
+
+        <item>@color/material_cyan_600</item>
+        <item>@color/material_light_blue_600</item>
+        <item>@color/material_blue_700</item>
+        <item>@color/material_indigo_600</item>
+
+        <item>@color/material_pink_500</item>
+        <item>@color/material_purple_600</item>
+        <item>@color/material_deep_purple_600</item>
+        <item>@color/material_blue_gray_700</item>
+    </integer-array>
+
+    <integer-array name="default_account_colors">
+        <item>@color/material_blue_700</item>
+        <item>@color/material_pink_500</item>
+        <item>@color/material_amber_600</item>
+    </integer-array>
+</resources>
diff --git a/legacy/core/src/main/res/values/material_colors.xml b/core/ui/legacy/theme2/common/src/main/res/values/material_colors.xml
similarity index 100%
rename from legacy/core/src/main/res/values/material_colors.xml
rename to core/ui/legacy/theme2/common/src/main/res/values/material_colors.xml
diff --git a/feature/account/avatar/src/debug/kotlin/app/k9mail/feature/account/avatar/ui/AvatarOutlinedPreview.kt b/feature/account/avatar/src/debug/kotlin/app/k9mail/feature/account/avatar/ui/AvatarOutlinedPreview.kt
new file mode 100644
index 00000000000..7f0f359cf66
--- /dev/null
+++ b/feature/account/avatar/src/debug/kotlin/app/k9mail/feature/account/avatar/ui/AvatarOutlinedPreview.kt
@@ -0,0 +1,29 @@
+package app.k9mail.feature.account.avatar.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+
+@Composable
+@Preview(showBackground = true)
+internal fun AvatarOutlinedPreview() {
+    PreviewWithThemes {
+        AvatarOutlined(
+            color = Color(0xFFe57373),
+            name = "example",
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun AvatarOutlinedLargePreview() {
+    PreviewWithThemes {
+        AvatarOutlined(
+            color = Color(0xFFe57373),
+            name = "example",
+            size = AvatarSize.LARGE,
+        )
+    }
+}
diff --git a/feature/account/avatar/src/main/kotlin/app/k9mail/feature/account/avatar/ui/AvatarOutlined.kt b/feature/account/avatar/src/main/kotlin/app/k9mail/feature/account/avatar/ui/AvatarOutlined.kt
new file mode 100644
index 00000000000..d6ebb4ccede
--- /dev/null
+++ b/feature/account/avatar/src/main/kotlin/app/k9mail/feature/account/avatar/ui/AvatarOutlined.kt
@@ -0,0 +1,118 @@
+package app.k9mail.feature.account.avatar.ui
+
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import app.k9mail.core.ui.compose.designsystem.atom.Surface
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleLarge
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
+import app.k9mail.core.ui.compose.theme2.ColorRoles
+import app.k9mail.core.ui.compose.theme2.MainTheme
+import app.k9mail.core.ui.compose.theme2.toColorRoles
+
+@Composable
+fun AvatarOutlined(
+    color: Color,
+    name: String,
+    modifier: Modifier = Modifier,
+    size: AvatarSize = AvatarSize.MEDIUM,
+    onClick: (() -> Unit)? = null,
+) {
+    val context = LocalContext.current
+    val colorRoles = color.toColorRoles(context)
+
+    AvatarLayout(
+        color = color,
+        colorRoles = colorRoles,
+        onClick = onClick,
+        modifier = modifier.size(getAvatarSize(size)),
+    ) {
+        AvatarPlaceholder(
+            displayName = name,
+            color = color,
+            size = size,
+        )
+        // TODO: Add image loading
+    }
+}
+
+@Composable
+private fun AvatarLayout(
+    color: Color,
+    colorRoles: ColorRoles,
+    modifier: Modifier = Modifier,
+    onClick: (() -> Unit)? = null,
+    content: @Composable BoxScope.() -> Unit,
+) {
+    Surface(
+        color = colorRoles.accentContainer,
+        modifier = modifier
+            .clip(CircleShape)
+            .border(
+                width = 2.dp,
+                shape = CircleShape,
+                color = color,
+            )
+            .clickable(
+                enabled = onClick != null,
+                onClick = { onClick?.invoke() },
+            ),
+    ) {
+        Box(
+            modifier = Modifier.fillMaxSize(),
+            contentAlignment = Alignment.Center,
+        ) {
+            content()
+        }
+    }
+}
+
+@Composable
+private fun AvatarPlaceholder(
+    color: Color,
+    displayName: String,
+    size: AvatarSize,
+    modifier: Modifier = Modifier,
+) {
+    when (size) {
+        AvatarSize.MEDIUM -> {
+            TextTitleMedium(
+                text = extractNameInitials(displayName).uppercase(),
+                color = color,
+                modifier = modifier,
+            )
+        }
+
+        AvatarSize.LARGE -> {
+            TextTitleLarge(
+                text = extractNameInitials(displayName).uppercase(),
+                color = color,
+                modifier = modifier,
+            )
+        }
+    }
+}
+
+@Composable
+private fun getAvatarSize(size: AvatarSize): Dp {
+    return when (size) {
+        AvatarSize.MEDIUM -> MainTheme.sizes.iconAvatar
+        AvatarSize.LARGE -> MainTheme.sizes.large
+    }
+}
+
+private fun extractNameInitials(displayName: String): String {
+    return displayName.take(2)
+}
diff --git a/feature/account/avatar/src/main/kotlin/app/k9mail/feature/account/avatar/ui/AvatarSize.kt b/feature/account/avatar/src/main/kotlin/app/k9mail/feature/account/avatar/ui/AvatarSize.kt
new file mode 100644
index 00000000000..fb840869aa1
--- /dev/null
+++ b/feature/account/avatar/src/main/kotlin/app/k9mail/feature/account/avatar/ui/AvatarSize.kt
@@ -0,0 +1,6 @@
+package app.k9mail.feature.account.avatar.ui
+
+enum class AvatarSize {
+    MEDIUM,
+    LARGE,
+}
diff --git a/feature/account/settings/impl/build.gradle.kts b/feature/account/settings/impl/build.gradle.kts
index 68351f2301d..e46a95ad25f 100644
--- a/feature/account/settings/impl/build.gradle.kts
+++ b/feature/account/settings/impl/build.gradle.kts
@@ -10,12 +10,14 @@ android {
 dependencies {
     api(projects.feature.account.settings.api)
     implementation(projects.feature.account.core)
+    implementation(projects.feature.account.avatar)
 
     implementation(projects.core.outcome)
 
     implementation(projects.core.ui.compose.designsystem)
     implementation(projects.core.ui.compose.navigation)
     implementation(projects.core.ui.compose.preference)
+    implementation(projects.core.ui.legacy.theme2.common)
 
     testImplementation(projects.core.ui.compose.testing)
 }
diff --git a/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContentPreview.kt b/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContentPreview.kt
new file mode 100644
index 00000000000..aa2380a7ba5
--- /dev/null
+++ b/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/GeneralSettingsContentPreview.kt
@@ -0,0 +1,20 @@
+package net.thunderbird.feature.account.settings.impl.ui.general
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
+import net.thunderbird.feature.account.settings.impl.ui.fake.FakePreferenceData
+
+@Composable
+@Preview(showBackground = true)
+internal fun GeneralSettingsContentPreview() {
+    PreviewWithTheme {
+        GeneralSettingsContent(
+            state = GeneralSettingsContract.State(
+                subtitle = "Subtitle",
+                preferences = FakePreferenceData.preferences,
+            ),
+            onEvent = {},
+        )
+    }
+}
diff --git a/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/components/GeneralSettingsProfileViewPreview.kt b/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/components/GeneralSettingsProfileViewPreview.kt
new file mode 100644
index 00000000000..ec5b4c18f33
--- /dev/null
+++ b/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/components/GeneralSettingsProfileViewPreview.kt
@@ -0,0 +1,32 @@
+package net.thunderbird.feature.account.settings.impl.ui.general.components
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+
+@Composable
+@Preview(showBackground = true)
+internal fun GeneralSettingsProfileViewPreview() {
+    PreviewWithThemes {
+        GeneralSettingsProfileView(
+            name = "Name",
+            email = "demo@example.com",
+            color = Color.Green,
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun GeneralSettingsProfileViewWithLongTextPreview() {
+    PreviewWithThemes {
+        GeneralSettingsProfileView(
+            name = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut " +
+                "labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " +
+                "nisi ut aliquip ex ea commodo consequat.",
+            email = "verylongemailaddress@exampledomainwithaverylongname.com",
+            color = Color.Green,
+        )
+    }
+}
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
index b756a85f4c4..6384a7abedb 100644
--- 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
@@ -1,5 +1,7 @@
 package net.thunderbird.feature.account.settings.impl.domain
 
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.coroutines.flow.Flow
@@ -29,9 +31,19 @@ internal interface AccountSettingsDomainContract {
 
     interface ResourceProvider {
         interface GeneralResourceProvider {
+            fun profileUi(
+                name: String,
+                color: Int,
+            ): @Composable (Modifier) -> Unit
+
             val nameTitle: () -> String
             val nameDescription: () -> String?
             val nameIcon: () -> ImageVector?
+
+            val colorTitle: () -> String
+            val colorDescription: () -> String?
+            val colorIcon: () -> ImageVector?
+            val colors: List<Int>
         }
     }
 
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
index ccc3017528f..3976ef5ee8f 100644
--- 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
@@ -3,7 +3,9 @@ package net.thunderbird.feature.account.settings.impl.domain.entity
 import net.thunderbird.feature.account.api.AccountId
 
 internal enum class GeneralPreference {
+    PROFILE,
     NAME,
+    COLOR,
 }
 
 internal fun GeneralPreference.generateId(accountId: AccountId): String {
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
index 053b6868c7c..791bfa934cf 100644
--- 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
@@ -6,6 +6,7 @@ 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.PreferenceDisplay
 import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
 import net.thunderbird.feature.account.api.AccountId
 import net.thunderbird.feature.account.api.profile.AccountProfile
@@ -37,6 +38,13 @@ internal class GetGeneralPreferences(
 
     private fun generatePreferences(accountId: AccountId, profile: AccountProfile): ImmutableList<Preference> {
         return persistentListOf(
+            PreferenceDisplay.Custom(
+                id = GeneralPreference.PROFILE.generateId(accountId),
+                customUi = resourceProvider.profileUi(
+                    name = profile.name,
+                    color = profile.color,
+                ),
+            ),
             PreferenceSetting.Text(
                 id = GeneralPreference.NAME.generateId(accountId),
                 title = resourceProvider.nameTitle,
@@ -44,6 +52,14 @@ internal class GetGeneralPreferences(
                 icon = resourceProvider.nameIcon,
                 value = profile.name,
             ),
+            PreferenceSetting.Color(
+                id = GeneralPreference.COLOR.generateId(accountId),
+                title = resourceProvider.colorTitle,
+                description = resourceProvider.colorDescription,
+                icon = resourceProvider.colorIcon,
+                value = profile.color,
+                colors = resourceProvider.colors,
+            ),
         )
     }
 }
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
index 041c938cac0..5743234c8da 100644
--- 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
@@ -25,6 +25,12 @@ internal class UpdateGeneralPreferences(
                 }
             }
 
+            GeneralPreference.COLOR.generateId(accountId) -> {
+                updateAccountProfile(accountId) {
+                    copy(color = preference.value as Int)
+                }
+            }
+
             else -> Outcome.failure(
                 SettingsError.NotFound(
                     message = "Unknown preference id: ${preference.id}",
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
index 71bad15153c..8c2131a2e12 100644
--- 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
@@ -1,17 +1,45 @@
 package net.thunderbird.feature.account.settings.impl.ui.general
 
 import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
 import net.thunderbird.feature.account.settings.R
 import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
+import net.thunderbird.feature.account.settings.impl.ui.general.components.GeneralSettingsProfileView
+import app.k9mail.core.ui.legacy.theme2.common.R as ThunderbirdCommonR
 
 internal class GeneralResourceProvider(
     private val context: Context,
 ) : ResourceProvider.GeneralResourceProvider {
 
+    override fun profileUi(
+        name: String,
+        color: Int,
+    ): @Composable ((Modifier) -> Unit) = { modifier ->
+        GeneralSettingsProfileView(
+            name = name,
+            email = null,
+            color = Color(color),
+            modifier = modifier,
+        )
+    }
+
     override val nameTitle: () -> String = {
         context.getString(R.string.account_settings_general_name_title)
     }
-    override val nameDescription: () -> String? = { null }
+    override val nameDescription: () -> String? = {
+        context.getString(R.string.account_settings_general_name_description)
+    }
     override val nameIcon: () -> ImageVector? = { null }
+
+    override val colorTitle: () -> String = {
+        context.getString(R.string.account_settings_general_color_title)
+    }
+    override val colorDescription: () -> String? = {
+        context.getString(R.string.account_settings_general_color_description)
+    }
+    override val colorIcon: () -> ImageVector? = { null }
+    override val colors: List<Int> = context.resources.getIntArray(ThunderbirdCommonR.array.account_colors).toList()
 }
diff --git a/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/components/GeneralSettingsProfileView.kt b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/components/GeneralSettingsProfileView.kt
new file mode 100644
index 00000000000..f52f325b463
--- /dev/null
+++ b/feature/account/settings/impl/src/main/kotlin/net/thunderbird/feature/account/settings/impl/ui/general/components/GeneralSettingsProfileView.kt
@@ -0,0 +1,87 @@
+package net.thunderbird.feature.account.settings.impl.ui.general.components
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import app.k9mail.core.ui.compose.designsystem.atom.card.CardElevated
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadlineSmall
+import app.k9mail.core.ui.compose.theme2.MainTheme
+import app.k9mail.feature.account.avatar.ui.AvatarOutlined
+import app.k9mail.feature.account.avatar.ui.AvatarSize
+
+@Composable
+internal fun GeneralSettingsProfileView(
+    name: String,
+    email: String?,
+    color: Color,
+    modifier: Modifier = Modifier,
+) {
+    Box(
+        modifier = modifier
+            .padding(MainTheme.spacings.double),
+        contentAlignment = Alignment.TopCenter,
+    ) {
+        ProfileCard(
+            name = name,
+            email = email,
+            modifier = Modifier
+                .padding(top = MainTheme.spacings.quadruple)
+                .fillMaxWidth(),
+        )
+        AvatarOutlined(
+            color = color,
+            name = name,
+            size = AvatarSize.LARGE,
+        )
+    }
+}
+
+@Composable
+private fun ProfileCard(
+    name: String,
+    email: String?,
+    modifier: Modifier = Modifier,
+) {
+    CardElevated(
+        modifier = modifier.fillMaxWidth(),
+    ) {
+        Column(
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(
+                    horizontal = MainTheme.spacings.oneHalf,
+                    vertical = MainTheme.spacings.triple,
+                ),
+        ) {
+            Spacer(modifier = Modifier.height(MainTheme.spacings.triple))
+            TextHeadlineSmall(
+                text = name,
+                color = MainTheme.colors.primary,
+                textAlign = TextAlign.Center,
+                modifier = Modifier.fillMaxWidth(),
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 1,
+            )
+            email?.let {
+                Spacer(modifier = Modifier.height(MainTheme.spacings.default))
+                TextBodyLarge(
+                    text = it,
+                    textAlign = TextAlign.Center,
+                    modifier = Modifier.fillMaxWidth(),
+                    overflow = TextOverflow.Ellipsis,
+                    maxLines = 1,
+                )
+            }
+        }
+    }
+}
diff --git a/feature/account/settings/impl/src/main/res/values/strings.xml b/feature/account/settings/impl/src/main/res/values/strings.xml
index 7c0c8f030ee..fc5cc8d7113 100644
--- a/feature/account/settings/impl/src/main/res/values/strings.xml
+++ b/feature/account/settings/impl/src/main/res/values/strings.xml
@@ -4,5 +4,9 @@
     <string name="account_settings_general_title">General Settings</string>
 
     <string name="account_settings_general_name_title">Account name</string>
+    <string name="account_settings_general_name_description">The name associated with your account.</string>
+
+    <string name="account_settings_general_color_title">Account color</string>
+    <string name="account_settings_general_color_description">The accent color of this account used in folders and account lists.</string>
 
 </resources>
diff --git a/legacy/common/src/main/java/com/fsck/k9/account/AccountModule.kt b/legacy/common/src/main/java/com/fsck/k9/account/AccountModule.kt
index f94e9fc2abf..c8198aa3ce6 100644
--- a/legacy/common/src/main/java/com/fsck/k9/account/AccountModule.kt
+++ b/legacy/common/src/main/java/com/fsck/k9/account/AccountModule.kt
@@ -4,7 +4,6 @@ import app.k9mail.feature.account.common.AccountCommonExternalContract
 import app.k9mail.feature.account.edit.AccountEditExternalContract
 import app.k9mail.feature.account.setup.AccountSetupExternalContract
 import app.k9mail.feature.settings.import.SettingsImportExternalContract
-import org.koin.android.ext.koin.androidApplication
 import org.koin.dsl.module
 
 val newAccountModule = module {
@@ -14,25 +13,6 @@ val newAccountModule = module {
         )
     }
 
-    factory {
-        AccountColorPicker(
-            accountManager = get(),
-            resources = get(),
-        )
-    }
-
-    factory<AccountSetupExternalContract.AccountCreator> {
-        AccountCreator(
-            accountColorPicker = get(),
-            localFoldersCreator = get(),
-            preferences = get(),
-            context = androidApplication(),
-            deletePolicyProvider = get(),
-            messagingController = get(),
-            unifiedInboxConfigurator = get(),
-        )
-    }
-
     factory<AccountCommonExternalContract.AccountStateLoader> {
         AccountStateLoader(
             accountManager = get(),
diff --git a/legacy/core/src/main/res/values/arrays_account_settings_values.xml b/legacy/core/src/main/res/values/arrays_account_settings_values.xml
index 3f5d0eacb19..c61769ea58b 100644
--- a/legacy/core/src/main/res/values/arrays_account_settings_values.xml
+++ b/legacy/core/src/main/res/values/arrays_account_settings_values.xml
@@ -1,34 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-    <integer-array name="account_colors">
-        <item>@color/material_amber_600</item>
-        <item>@color/material_orange_600</item>
-        <item>@color/material_deep_orange_600</item>
-        <item>@color/material_red_600</item>
-
-        <item>@color/material_lime_600</item>
-        <item>@color/material_light_green_600</item>
-        <item>@color/material_green_700</item>
-        <item>@color/material_teal_600</item>
-
-        <item>@color/material_cyan_600</item>
-        <item>@color/material_light_blue_600</item>
-        <item>@color/material_blue_700</item>
-        <item>@color/material_indigo_600</item>
-
-        <item>@color/material_pink_500</item>
-        <item>@color/material_purple_600</item>
-        <item>@color/material_deep_purple_600</item>
-        <item>@color/material_blue_gray_700</item>
-    </integer-array>
-
-    <integer-array name="default_account_colors">
-        <item>@color/material_blue_700</item>
-        <item>@color/material_pink_500</item>
-        <item>@color/material_amber_600</item>
-    </integer-array>
-
     <string-array name="check_frequency_values" translatable="false">
         <item>-1</item>
         <item>15</item>


Test patch:
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 a0aff575e16..f857dbbeea9 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
@@ -4,7 +4,7 @@ import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
 import kotlinx.collections.immutable.persistentListOf
 import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
 
-object FakePreferenceData {
+internal object FakePreferenceData {
 
     val textPreference = PreferenceSetting.Text(
         id = "text",
@@ -14,7 +14,21 @@ object FakePreferenceData {
         value = "Value",
     )
 
+    val colorPreference = PreferenceSetting.Color(
+        id = "color",
+        icon = { Icons.Outlined.Delete },
+        title = { "Title" },
+        description = { "Description" },
+        value = 0xFFFF0000.toInt(),
+        colors = listOf(
+            0xFFFF0000.toInt(),
+            0xFF00FF00.toInt(),
+            0xFF0000FF.toInt(),
+        ),
+    )
+
     val preferences = persistentListOf(
         textPreference,
+        colorPreference,
     )
 }
diff --git a/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/fake/FakePreferenceData.kt b/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/fake/FakePreferenceData.kt
new file mode 100644
index 00000000000..0da71747313
--- /dev/null
+++ b/feature/account/settings/impl/src/debug/kotlin/net/thunderbird/feature/account/settings/impl/ui/fake/FakePreferenceData.kt
@@ -0,0 +1,58 @@
+package net.thunderbird.feature.account.settings.impl.ui.fake
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import app.k9mail.core.ui.compose.designsystem.atom.card.CardElevated
+import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
+import app.k9mail.core.ui.compose.theme2.MainTheme
+import kotlinx.collections.immutable.persistentListOf
+import net.thunderbird.core.ui.compose.preference.api.PreferenceDisplay
+import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
+
+object FakePreferenceData {
+
+    val textPreference = PreferenceSetting.Text(
+        id = "text",
+        icon = { Icons.Outlined.Info },
+        title = { "Title" },
+        description = { "Description" },
+        value = "Value",
+    )
+
+    val colorPreference = PreferenceSetting.Color(
+        id = "color",
+        icon = { Icons.Outlined.Info },
+        title = { "Title" },
+        description = { "Description" },
+        value = 0xFFFF0000.toInt(),
+        colors = listOf(
+            0xFFFF0000.toInt(),
+            0xFF00FF00.toInt(),
+            0xFF0000FF.toInt(),
+        ),
+    )
+
+    val customPreference = PreferenceDisplay.Custom(
+        id = "custom",
+        customUi = { modifier ->
+            CardElevated(
+                modifier = modifier.fillMaxWidth()
+                    .padding(MainTheme.spacings.double),
+            ) {
+                TextBodyLarge(
+                    text = "Custom UI",
+                    modifier = Modifier.padding(MainTheme.spacings.default)
+                        .fillMaxWidth(),
+                )
+            }
+        },
+    )
+
+    val preferences = persistentListOf(
+        textPreference,
+        colorPreference,
+        customPreference,
+    )
+}
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
index 402cd76b8ff..62d2cfb8548 100644
--- 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
@@ -1,10 +1,22 @@
 package net.thunderbird.feature.account.settings.impl.domain.usecase
 
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import net.thunderbird.feature.account.settings.impl.domain.AccountSettingsDomainContract.ResourceProvider
 
 internal class FakeGeneralResourceProvider : ResourceProvider.GeneralResourceProvider {
+    override fun profileUi(
+        name: String,
+        color: Int,
+    ): @Composable ((Modifier) -> Unit) = { }
+
     override val nameTitle: () -> String = { "Name" }
     override val nameDescription: () -> String? = { null }
     override val nameIcon: () -> ImageVector? = { null }
+
+    override val colorTitle: () -> String = { "Color" }
+    override val colorDescription: () -> String? = { null }
+    override val colorIcon: () -> ImageVector? = { null }
+    override val colors: List<Int> = listOf(0xFF0000, 0x00FF00, 0x0000FF)
 }
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
index eacaecc2dfe..e4d7cf82dc1 100644
--- 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
@@ -8,6 +8,7 @@ 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.PreferenceDisplay
 import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
 import net.thunderbird.feature.account.api.AccountId
 import net.thunderbird.feature.account.api.profile.AccountProfile
@@ -37,6 +38,13 @@ internal class GetGeneralPreferencesTest {
             val success = outcome as Outcome.Success
             assertThat(success.data).isEqualTo(
                 persistentListOf(
+                    PreferenceDisplay.Custom(
+                        id = "${accountId.value}-general-profile",
+                        customUi = resourceProvider.profileUi(
+                            name = accountProfile.name,
+                            color = accountProfile.color,
+                        ),
+                    ),
                     PreferenceSetting.Text(
                         id = "${accountId.value}-general-name",
                         title = resourceProvider.nameTitle,
@@ -44,6 +52,14 @@ internal class GetGeneralPreferencesTest {
                         icon = resourceProvider.nameIcon,
                         value = accountProfile.name,
                     ),
+                    PreferenceSetting.Color(
+                        id = "${accountId.value}-general-color",
+                        title = resourceProvider.colorTitle,
+                        description = resourceProvider.colorDescription,
+                        icon = resourceProvider.colorIcon,
+                        value = accountProfile.color,
+                        colors = resourceProvider.colors,
+                    ),
                 ),
             )
         }
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
index 00c8ed7c035..1bbef16c52c 100644
--- 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
@@ -48,6 +48,53 @@ class UpdateGeneralPreferencesTest {
         )
     }
 
+    @Test
+    fun `should update account profile for all general settings`() = runTest {
+        // Arrange
+        val accountId = AccountId.create()
+        val accountProfile = AccountProfile(
+            accountId = accountId,
+            name = "Test Account",
+            color = 0xFF0000,
+        )
+        val newName = "Updated Account Name"
+        val newColor = 0x00FF00
+        val preferences = listOf(
+            PreferenceSetting.Text(
+                id = GeneralPreference.NAME.generateId(accountId),
+                title = { "Name" },
+                description = { "Account name" },
+                icon = { null },
+                value = newName,
+            ),
+            PreferenceSetting.Color(
+                id = GeneralPreference.COLOR.generateId(accountId),
+                title = { "Color" },
+                description = { "Account color" },
+                icon = { null },
+                value = newColor,
+                colors = listOf(0xFF0000, 0x00FF00, 0x0000FF),
+            ),
+        )
+        val repository = FakeAccountProfileRepository(
+            initialAccountProfile = accountProfile,
+        )
+        val testSubject = UpdateGeneralPreferences(repository)
+
+        // Act
+        preferences.forEach { preference ->
+            testSubject(accountId, preference)
+        }
+
+        // Assert
+        assertThat(repository.getById(accountId).firstOrNull()).isEqualTo(
+            accountProfile.copy(
+                name = newName,
+                color = newColor,
+            ),
+        )
+    }
+
     @Test
     fun `should emit NotFound when account profile not found`() = runTest {
         // Arrange