8000 fix(avatar): add missing storage migration to prepopulate the avatar monogram by wmontwe · Pull Request #9332 · thunderbird/thunderbird-android · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix(avatar): add missing storage migration to prepopulate the avatar monogram #9332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependencies {
implementation(projects.core.featureflag)
implementation(projects.core.ui.legacy.theme2.common)

implementation(projects.feature.account.avatar.api)
implementation(projects.feature.account.avatar.impl)
implementation(projects.feature.account.setup)
implementation(projects.feature.mail.account.api)
implementation(projects.feature.migration.provider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,23 @@ import kotlinx.coroutines.withContext
import net.thunderbird.core.android.account.LegacyAccount
import net.thunderbird.core.common.mail.Protocols
import net.thunderbird.core.logging.legacy.Log
import net.thunderbird.feature.account.avatar.AvatarMonogramCreator
import net.thunderbird.feature.account.storage.profile.AvatarDto
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection

// TODO Move to feature/account/setup
@Suppress("LongParameterList")
internal class AccountCreator(
private val accountColorPicker: AccountColorPicker,
private val localFoldersCreator: SpecialLocalFoldersCreator,
private val preferences: Preferences,
private val context: Context,
private val messagingController: MessagingController,
private val deletePolicyProvider: DeletePolicyProvider,
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
private val avatarMonogramCreator: AvatarMonogramCreator,
private val unifiedInboxConfigurator: UnifiedInboxConfigurator,
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : AccountSetupExternalContract.AccountCreator {

@Suppress("TooGenericExceptionCaught")
Expand All @@ -54,6 +59,13 @@ internal class AccountCreator(

newAccount.email = account.emailAddress

newAccount.avatar = AvatarDto(
avatarType = AvatarTypeDto.MONOGRAM,
avatarMonogram = avatarMonogramCreator.create(account.options.accountName, account.emailAddress),
avatarImageUri = null,
avatarIconName = null,
)

newAccount.setIncomingServerSettings(account.incomingServerSettings)
newAccount.outgoingServerSettings = account.outgoingServerSettings

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import net.thunderbird.app.common.account.data.DefaultAccountProfileLocalDataSou
import net.thunderbird.app.common.account.data.DefaultLegacyAccountWrapperManager
import net.thunderbird.core.android.account.AccountDefaultsProvider
import net.thunderbird.core.android.account.LegacyAccountWrapperManager
import net.thunderbird.feature.account.avatar.AvatarMonogramCreator
import net.thunderbird.feature.account.avatar.DefaultAvatarMonogramCreator
import net.thunderbird.feature.account.core.AccountCoreExternalContract.AccountProfileLocalDataSource
import net.thunderbird.feature.account.core.featureAccountCoreModule
import net.thunderbird.feature.account.storage.legacy.featureAccountStorageLegacyModule
Expand Down Expand Up @@ -45,6 +47,10 @@ internal val appCommonAccountModule = module {
)
}

factory<AvatarMonogramCreator> {
DefaultAvatarMonogramCreator()
}

factory<AccountSetupExternalContract.AccountCreator> {
AccountCreator(
accountColorPicker = get(),
Expand All @@ -53,6 +59,7 @@ internal val appCommonAccountModule = module {
context = androidApplication(),
deletePolicyProvider = get(),
messagingController = get(),
avatarMonogramCreator = get(),
unifiedInboxConfigurator = get(),
)
}
Expand Down
7 changes: 7 additions & 0 deletions feature/account/avatar/api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id(ThunderbirdPlugins.Library.kmp)
}

android {
namespace = "net.thunderbird.feature.account.avatar"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package net.thunderbird.feature.account.avatar

/**
* Interface for creating a monogram based on a name or email address.
*
* This interface is used to generate a monogram, which is typically the initials of a person's name,
* or a representation based on an email address. Implementations should handle null or empty inputs gracefully.
*/
fun interface AvatarMonogramCreator {
/**
* Creates a monogram for the given name or email.
*
* @param name The name to generate a monogram for.
* @param email The email address to generate a monogram for.
* @return A string representing the monogram, or an empty string if the name or email is null or empty.
*/
fun create(name: String?, email: String?): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ plugins {
}

android {
namespace = "app.k9mail.feature.account.avatar"
namespace = "net.thunderbird.feature.account.avatar.impl"
resourcePrefix = "account_avatar_"
}

dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.common)

implementation(projects.feature.account.avatar.api)

testImplementation(projects.core.ui.compose.testing)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.k9mail.feature.account.avatar.ui
package net.thunderbird.feature.account.avatar.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.k9mail.feature.account.avatar.ui
package net.thunderbird.feature.account.avatar.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.thunderbird.feature.account.avatar

/**
* Creates a monogram based on a name or email address.
*
* This implementation generates a monogram by taking the first two characters of the name or email,
* removing spaces, and converting them to uppercase.
*/
class DefaultAvatarMonogramCreator : AvatarMonogramCreator {
override fun create(name: String?, email: String?): String {
return if (name != null && name.isNotEmpty()) {
composeAvatarMonogram(name)
} else if (email != null && email.isNotEmpty()) {
composeAvatarMonogram(email)
} else {
AVATAR_MONOGRAM_DEFAULT
}
}

private fun composeAvatarMonogram(name: String): String {
return name.replace(" ", "").take(2).uppercase()
}

private companion object {
private const val AVATAR_MONOGRAM_DEFAULT = "XX"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.k9mail.feature.account.avatar.ui
package net.thunderbird.feature.account.avatar.ui

import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.border
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.k9mail.feature.account.avatar.ui
package net.thunderbird.feature.account.avatar.ui

import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.k9mail.feature.account.avatar.ui
package net.thunderbird.feature.account.avatar.ui

enum class AvatarSize {
MEDIUM,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.thunderbird.feature.account.avatar

import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlin.test.Test

class DefaultAvatarMonogramCreatorTest {

private val testSubject = DefaultAvatarMonogramCreator()

@Test
fun `create returns correct monogram for name`() {
val name = "John Doe"
val expectedMonogram = "JO"

val result = testSubject.create(name, null)

assertThat(result).isEqualTo(expectedMonogram)
}

@Test
fun `create returns correct monogram for email`() {
val email = "test@example.com"
val expectedMonogram = "TE"

val result = testSubject.create(null, email)

assertThat(result).isEqualTo(expectedMonogram)
}

@Test
fun `create returns default monogram for null or empty inputs`() {
val expectedMonogram = "XX"

val resultWithNulls = testSubject.create(null, null)
assertThat(resultWithNulls).isEqualTo(expectedMonogram)

val resultWithEmptyStrings = testSubject.create("", "")
assertThat(resultWithEmptyStrings).isEqualTo(expectedMonogram)
}
}
2 changes: 1 addition & 1 deletion feature/account/settings/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {
dependencies {
api(projects.feature.account.settings.api)
implementation(projects.feature.account.core)
implementation(projects.feature.account.avatar)
implementation(projects.feature.account.avatar.impl)

implementation(projects.core.outcome)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ 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
import net.thunderbird.feature.account.avatar.ui.AvatarOutlined
import net.thunderbird.feature.account.avatar.ui.AvatarSize

@Composable
internal fun GeneralSettingsProfileView(
Expand Down
2 changes: 1 addition & 1 deletion feature/navigation/drawer/dropdown/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies {
implementation(projects.core.ui.theme.api)
implementation(projects.core.ui.compose.designsystem)

implementation(projects.feature.account.avatar)
implementation(projects.feature.account.avatar.impl)
implementation(projects.feature.mail.account.api)
implementation(projects.feature.mail.folder.api)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelSmall
import app.k9mail.core.ui.compose.theme2.ColorRoles
import app.k9mail.core.ui.compose.theme2.toColorRoles
import app.k9mail.feature.account.avatar.ui.Avatar
import net.thunderbird.feature.account.avatar.ui.Avatar
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.labelForCount

Expand Down
2 changes: 1 addition & 1 deletion feature/navigation/drawer/siderail/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies {
implementation(projects.core.ui.theme.api)
implementation(projects.core.ui.compose.designsystem)

implementation(projects.feature.account.avatar)
implementation(projects.feature.account.avatar.impl)
implementation(projects.feature.mail.account.api)
implementation(projects.feature.mail.folder.api)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelSmall
import app.k9mail.core.ui.compose.theme2.ColorRoles
import app.k9mail.core.ui.compose.theme2.toColorRoles
import app.k9mail.feature.account.avatar.ui.Avatar
import net.thunderbird.feature.account.avatar.ui.Avatar
import net.thunderbird.feature.navigation.drawer.siderail.domain.entity.DisplayAccount
import net.thunderbird.feature.navigation.drawer.siderail.ui.common.labelForCount

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import net.thunderbird.core.preference.storage.StorageUpdater;

public class K9StoragePersister implements StoragePersister {
private static final int DB_VERSION = 26;
private static final int DB_VERSION = 27;
private static final String DB_NAME = "preferences_storage";

private final Context context;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.fsck.k9.preferences.migration

import android.database.sqlite.SQLiteDatabase
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto

/**
* Migration to add avatar monograms for accounts that have the MONOGRAM avatar type
* and do not have an existing avatar monogram.
*/
class StorageMigrationTo27(
private val db: SQLiteDatabase,
private val migrationsHelper: StorageMigrationHelper,
) {
fun addAvatarMonogram() {
val accountUuidsValue = migrationsHelper.readValue(db, "accountUuids")
if (accountUuidsValue.isNullOrEmpty()) {
return
}

val accountUuids = accountUuidsValue.split(",")
for (accountUuid in accountUuids) {
addAvatarMonogramToAccount(accountUuid)
}
}

private fun addAvatarMonogramToAccount(accountUuid: String) {
val avatarType = readAvatarType(accountUuid)
val avatarMonogram = readAvatarMonogram(accountUuid)

if (avatarType == AvatarTypeDto.MONOGRAM.name && avatarMonogram.isEmpty()) {
val monogram = generateAvatarMonogram(accountUuid)
insertAvatarMonogram(accountUuid, monogram)
}
}

private fun generateAvatarMonogram(accountUuid: String): String {
val name = readName(accountUuid)
val email = readEmail(accountUuid)
return getAvatarMonogram(name, email)
}

private fun getAvatarMonogram(name: String?, email: String?): String {
return if (name != null && name.isNotEmpty()) {
composeAvatarMonogram(name)
} else if (email != null && email.isNotEmpty()) {
composeAvatarMonogram(email)
} else {
AVATAR_MONOGRAM_DEFAULT
}
}

private fun composeAvatarMonogram(name: String): String {
return name.replace(" ", "").take(2).uppercase()
}

private fun readAvatarType(accountUuid: String): String {
return migrationsHelper.readValue(db, "$accountUuid.$AVATAR_TYPE_KEY") ?: ""
}

private fun readAvatarMonogram(accountUuid: String): String {
return migrationsHelper.readValue(db, "$accountUuid.$AVATAR_MONOGRAM_KEY") ?: ""
}

private fun readName(accountUuid: String): String {
return migrationsHelper.readValue(db, "$accountUuid.$NAME_KEY") ?: ""
}

private fun readEmail(accountUuid: String): String {
return migrationsHelper.readValue(db, "$accountUuid.$EMAIL_KEY") ?: ""
}

private fun insertAvatarMonogram(accountUuid: String, monogram: String) {
migrationsHelper.insertValue(db, "$accountUuid.$AVATAR_MONOGRAM_KEY", monogram)
}

private companion object {
const val NAME_KEY = "name.0"
const val EMAIL_KEY = "email.0"
const val AVATAR_TYPE_KEY = "avatarType"
const val AVATAR_MONOGRAM_KEY = "avatarMonogram"

private const val AVATAR_MONOGRAM_DEFAULT = "XX"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ internal object StorageMigrations {
if (oldVersion < 24) StorageMigrationTo24(db, migrationsHelper).removeLegacyAuthenticationModes()
if (oldVersion < 25) StorageMigrationTo25(db, migrationsHelper).convertToAuthTypeNone()
if (oldVersion < 26) StorageMigrationTo26(db, migrationsHelper).fixIdentities()
if (oldVersion < 27) StorageMigrationTo27(db, migrationsHelper).addAvatarMonogram()
}
}
Loading
0