Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
27
feature/account/storage/legacy/build.gradle.kts
Normal file
27
feature/account/storage/legacy/build.gradle.kts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.android)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "net.thunderbird.feature.account.storage.legacy"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.feature.account.storage.api)
|
||||
|
||||
implementation(projects.feature.notification.api)
|
||||
implementation(projects.feature.mail.account.api)
|
||||
implementation(projects.feature.mail.folder.api)
|
||||
|
||||
implementation(projects.core.logging.api)
|
||||
implementation(projects.core.preference.api)
|
||||
|
||||
implementation(projects.mail.common)
|
||||
|
||||
implementation(projects.core.android.account)
|
||||
|
||||
implementation(libs.moshi)
|
||||
|
||||
testImplementation(projects.feature.account.fake)
|
||||
testImplementation(projects.mail.protocols.imap)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
|
||||
/**
|
||||
* Generates keys for account storage.
|
||||
*/
|
||||
class AccountKeyGenerator(
|
||||
private val id: AccountId,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Creates a key by combining account ID with the specified key.
|
||||
*
|
||||
* @param key The key to combine with the account ID.
|
||||
* @throws IllegalArgumentException if the key is empty.
|
||||
*/
|
||||
fun create(key: String): String {
|
||||
require(key.isNotEmpty()) { "Key must not be empty" }
|
||||
return "${id.asRaw()}.$key"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import net.thunderbird.feature.account.storage.legacy.mapper.DefaultAccountAvatarDataMapper
|
||||
import net.thunderbird.feature.account.storage.legacy.mapper.DefaultAccountProfileDataMapper
|
||||
import net.thunderbird.feature.account.storage.legacy.mapper.DefaultLegacyAccountWrapperDataMapper
|
||||
import net.thunderbird.feature.account.storage.legacy.serializer.ServerSettingsDtoSerializer
|
||||
import net.thunderbird.feature.account.storage.mapper.AccountAvatarDataMapper
|
||||
import net.thunderbird.feature.account.storage.mapper.AccountProfileDataMapper
|
||||
import org.koin.dsl.module
|
||||
|
||||
val featureAccountStorageLegacyModule = module {
|
||||
factory {
|
||||
DefaultLegacyAccountWrapperDataMapper()
|
||||
}
|
||||
|
||||
factory<AccountAvatarDataMapper> {
|
||||
DefaultAccountAvatarDataMapper()
|
||||
}
|
||||
|
||||
factory<AccountProfileDataMapper> {
|
||||
DefaultAccountProfileDataMapper(
|
||||
avatarMapper = get(),
|
||||
)
|
||||
}
|
||||
|
||||
factory { ServerSettingsDtoSerializer() }
|
||||
|
||||
factory<AvatarDtoStorageHandler> {
|
||||
LegacyAvatarDtoStorageHandler()
|
||||
}
|
||||
|
||||
factory<ProfileDtoStorageHandler> {
|
||||
LegacyProfileDtoStorageHandler(
|
||||
avatarDtoStorageHandler = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<AccountDtoStorageHandler> {
|
||||
LegacyAccountStorageHandler(
|
||||
serverSettingsDtoSerializer = get(),
|
||||
profileDtoStorageHandler = get(),
|
||||
logger = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,611 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import net.thunderbird.core.android.account.AccountDefaultsProvider
|
||||
import net.thunderbird.core.android.account.DeletePolicy
|
||||
import net.thunderbird.core.android.account.Expunge
|
||||
import net.thunderbird.core.android.account.FolderMode
|
||||
import net.thunderbird.core.android.account.Identity
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.android.account.MessageFormat
|
||||
import net.thunderbird.core.android.account.QuoteStyle
|
||||
import net.thunderbird.core.android.account.ShowPictures
|
||||
import net.thunderbird.core.android.account.SortType
|
||||
import net.thunderbird.core.logging.Logger
|
||||
import net.thunderbird.core.preference.storage.Storage
|
||||
import net.thunderbird.core.preference.storage.StorageEditor
|
||||
import net.thunderbird.core.preference.storage.getEnumOrDefault
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
import net.thunderbird.feature.account.storage.legacy.serializer.ServerSettingsDtoSerializer
|
||||
import net.thunderbird.feature.mail.folder.api.FOLDER_DEFAULT_PATH_DELIMITER
|
||||
import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection
|
||||
import net.thunderbird.feature.notification.NotificationLight
|
||||
import net.thunderbird.feature.notification.NotificationSettings
|
||||
import net.thunderbird.feature.notification.NotificationVibration
|
||||
import net.thunderbird.feature.notification.VibratePattern
|
||||
|
||||
class LegacyAccountStorageHandler(
|
||||
private val serverSettingsDtoSerializer: ServerSettingsDtoSerializer,
|
||||
private val profileDtoStorageHandler: ProfileDtoStorageHandler,
|
||||
private val logger: Logger,
|
||||
) : AccountDtoStorageHandler {
|
||||
|
||||
@Suppress("LongMethod", "MagicNumber")
|
||||
@Synchronized
|
||||
override fun load(data: LegacyAccount, storage: Storage) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
profileDtoStorageHandler.load(data, storage)
|
||||
|
||||
with(data) {
|
||||
incomingServerSettings = serverSettingsDtoSerializer.deserialize(
|
||||
storage.getStringOrDefault(keyGen.create(INCOMING_SERVER_SETTINGS_KEY), ""),
|
||||
)
|
||||
outgoingServerSettings = serverSettingsDtoSerializer.deserialize(
|
||||
storage.getStringOrDefault(keyGen.create(OUTGOING_SERVER_SETTINGS_KEY), ""),
|
||||
)
|
||||
oAuthState = storage.getStringOrNull(keyGen.create("oAuthState"))
|
||||
alwaysBcc = storage.getStringOrNull(keyGen.create("alwaysBcc")) ?: alwaysBcc
|
||||
automaticCheckIntervalMinutes = storage.getInt(
|
||||
keyGen.create("automaticCheckIntervalMinutes"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_SYNC_INTERVAL,
|
||||
)
|
||||
idleRefreshMinutes = storage.getInt(keyGen.create("idleRefreshMinutes"), 24)
|
||||
displayCount = storage.getInt(
|
||||
keyGen.create("displayCount"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_VISIBLE_LIMIT,
|
||||
)
|
||||
if (displayCount < 0) {
|
||||
displayCount = AccountDefaultsProvider.Companion.DEFAULT_VISIBLE_LIMIT
|
||||
}
|
||||
isNotifyNewMail = storage.getBoolean(keyGen.create("notifyNewMail"), false)
|
||||
folderNotifyNewMailMode = getEnumStringPref<FolderMode>(
|
||||
storage,
|
||||
keyGen.create("folderNotifyNewMailMode"),
|
||||
FolderMode.ALL,
|
||||
)
|
||||
isNotifySelfNewMail = storage.getBoolean(keyGen.create("notifySelfNewMail"), true)
|
||||
isNotifyContactsMailOnly = storage.getBoolean(keyGen.create("notifyContactsMailOnly"), false)
|
||||
isIgnoreChatMessages = storage.getBoolean(keyGen.create("ignoreChatMessages"), false)
|
||||
isNotifySync = storage.getBoolean(keyGen.create("notifyMailCheck"), false)
|
||||
messagesNotificationChannelVersion = storage.getInt(keyGen.create("messagesNotificationChannelVersion"), 0)
|
||||
deletePolicy = DeletePolicy.Companion.fromInt(
|
||||
storage.getInt(
|
||||
keyGen.create("deletePolicy"),
|
||||
DeletePolicy.NEVER.setting,
|
||||
),
|
||||
)
|
||||
legacyInboxFolder = storage.getStringOrNull(keyGen.create("inboxFolderName"))
|
||||
importedDraftsFolder = storage.getStringOrNull(keyGen.create("draftsFolderName"))
|
||||
importedSentFolder = storage.getStringOrNull(keyGen.create("sentFolderName"))
|
||||
importedTrashFolder = storage.getStringOrNull(keyGen.create("trashFolderName"))
|
||||
importedArchiveFolder = storage.getStringOrNull(keyGen.create("archiveFolderName"))
|
||||
importedSpamFolder = storage.getStringOrNull(keyGen.create("spamFolderName"))
|
||||
|
||||
inboxFolderId = storage.getStringOrNull(keyGen.create("inboxFolderId"))?.toLongOrNull()
|
||||
|
||||
val draftsFolderId = storage.getStringOrNull(keyGen.create("draftsFolderId"))?.toLongOrNull()
|
||||
val draftsFolderSelection = getEnumStringPref<SpecialFolderSelection>(
|
||||
storage,
|
||||
keyGen.create("draftsFolderSelection"),
|
||||
SpecialFolderSelection.AUTOMATIC,
|
||||
)
|
||||
setDraftsFolderId(draftsFolderId, draftsFolderSelection)
|
||||
|
||||
val sentFolderId = storage.getStringOrNull(keyGen.create("sentFolderId"))?.toLongOrNull()
|
||||
val sentFolderSelection = getEnumStringPref<SpecialFolderSelection>(
|
||||
storage,
|
||||
keyGen.create("sentFolderSelection"),
|
||||
SpecialFolderSelection.AUTOMATIC,
|
||||
)
|
||||
setSentFolderId(sentFolderId, sentFolderSelection)
|
||||
|
||||
val trashFolderId = storage.getStringOrNull(keyGen.create("trashFolderId"))?.toLongOrNull()
|
||||
val trashFolderSelection = getEnumStringPref<SpecialFolderSelection>(
|
||||
storage,
|
||||
keyGen.create("trashFolderSelection"),
|
||||
SpecialFolderSelection.AUTOMATIC,
|
||||
)
|
||||
setTrashFolderId(trashFolderId, trashFolderSelection)
|
||||
|
||||
val archiveFolderId = storage.getStringOrNull(keyGen.create("archiveFolderId"))?.toLongOrNull()
|
||||
val archiveFolderSelection = getEnumStringPref<SpecialFolderSelection>(
|
||||
storage,
|
||||
keyGen.create("archiveFolderSelection"),
|
||||
SpecialFolderSelection.AUTOMATIC,
|
||||
)
|
||||
setArchiveFolderId(archiveFolderId, archiveFolderSelection)
|
||||
|
||||
val spamFolderId = storage.getStringOrNull(keyGen.create("spamFolderId"))?.toLongOrNull()
|
||||
val spamFolderSelection = getEnumStringPref<SpecialFolderSelection>(
|
||||
storage,
|
||||
keyGen.create("spamFolderSelection"),
|
||||
SpecialFolderSelection.AUTOMATIC,
|
||||
)
|
||||
setSpamFolderId(spamFolderId, spamFolderSelection)
|
||||
|
||||
autoExpandFolderId = storage.getStringOrNull(keyGen.create("autoExpandFolderId"))?.toLongOrNull()
|
||||
|
||||
expungePolicy = getEnumStringPref(storage, keyGen.create("expungePolicy"), Expunge.EXPUNGE_IMMEDIATELY)
|
||||
isSyncRemoteDeletions = storage.getBoolean(keyGen.create("syncRemoteDeletions"), true)
|
||||
|
||||
maxPushFolders = storage.getInt(keyGen.create("maxPushFolders"), 10)
|
||||
isSubscribedFoldersOnly = storage.getBoolean(keyGen.create("subscribedFoldersOnly"), false)
|
||||
maximumPolledMessageAge = storage.getInt(keyGen.create("maximumPolledMessageAge"), -1)
|
||||
maximumAutoDownloadMessageSize = storage.getInt(
|
||||
keyGen.create("maximumAutoDownloadMessageSize"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_MAXIMUM_AUTO_DOWNLOAD_MESSAGE_SIZE,
|
||||
)
|
||||
messageFormat = getEnumStringPref(
|
||||
storage,
|
||||
keyGen.create("messageFormat"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_MESSAGE_FORMAT,
|
||||
)
|
||||
val messageFormatAuto = storage.getBoolean(
|
||||
keyGen.create("messageFormatAuto"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_MESSAGE_FORMAT_AUTO,
|
||||
)
|
||||
if (messageFormatAuto && messageFormat == MessageFormat.TEXT) {
|
||||
messageFormat = MessageFormat.AUTO
|
||||
}
|
||||
isMessageReadReceipt = storage.getBoolean(
|
||||
keyGen.create("messageReadReceipt"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_MESSAGE_READ_RECEIPT,
|
||||
)
|
||||
quoteStyle = getEnumStringPref<QuoteStyle>(
|
||||
storage,
|
||||
keyGen.create("quoteStyle"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_QUOTE_STYLE,
|
||||
)
|
||||
quotePrefix = storage.getStringOrDefault(
|
||||
keyGen.create("quotePrefix"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_QUOTE_PREFIX,
|
||||
)
|
||||
isDefaultQuotedTextShown = storage.getBoolean(
|
||||
keyGen.create("defaultQuotedTextShown"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_QUOTED_TEXT_SHOWN,
|
||||
)
|
||||
isReplyAfterQuote = storage.getBoolean(
|
||||
keyGen.create("replyAfterQuote"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_REPLY_AFTER_QUOTE,
|
||||
)
|
||||
isStripSignature = storage.getBoolean(
|
||||
keyGen.create("stripSignature"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_STRIP_SIGNATURE,
|
||||
)
|
||||
useCompression = storage.getBoolean(keyGen.create("useCompression"), true)
|
||||
isSendClientInfoEnabled = storage.getBoolean(keyGen.create("sendClientInfo"), true)
|
||||
|
||||
importedAutoExpandFolder = storage.getStringOrNull(keyGen.create("autoExpandFolderName"))
|
||||
|
||||
accountNumber = storage.getInt(
|
||||
keyGen.create("accountNumber"),
|
||||
AccountDefaultsProvider.Companion.UNASSIGNED_ACCOUNT_NUMBER,
|
||||
)
|
||||
|
||||
sortType = getEnumStringPref<SortType>(storage, keyGen.create("sortTypeEnum"), SortType.SORT_DATE)
|
||||
|
||||
setSortAscending(sortType, storage.getBoolean(keyGen.create("sortAscending"), false))
|
||||
|
||||
showPictures =
|
||||
getEnumStringPref<ShowPictures>(storage, keyGen.create("showPicturesEnum"), ShowPictures.NEVER)
|
||||
|
||||
updateNotificationSettings {
|
||||
NotificationSettings(
|
||||
isRingEnabled = storage.getBoolean(keyGen.create("ring"), true),
|
||||
ringtone = storage.getStringOrDefault(
|
||||
keyGen.create("ringtone"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_RINGTONE_URI,
|
||||
),
|
||||
light = getEnumStringPref(
|
||||
storage,
|
||||
keyGen.create("notificationLight"),
|
||||
NotificationLight.Disabled,
|
||||
),
|
||||
vibration = NotificationVibration(
|
||||
isEnabled = storage.getBoolean(keyGen.create("vibrate"), false),
|
||||
pattern = VibratePattern.Companion.deserialize(
|
||||
storage.getInt(
|
||||
keyGen.create("vibratePattern"),
|
||||
0,
|
||||
),
|
||||
),
|
||||
repeatCount = storage.getInt(keyGen.create("vibrateTimes"), 5),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
folderDisplayMode =
|
||||
getEnumStringPref<FolderMode>(storage, keyGen.create("folderDisplayMode"), FolderMode.NOT_SECOND_CLASS)
|
||||
|
||||
folderSyncMode =
|
||||
getEnumStringPref<FolderMode>(storage, keyGen.create("folderSyncMode"), FolderMode.FIRST_CLASS)
|
||||
|
||||
folderPushMode = getEnumStringPref<FolderMode>(storage, keyGen.create("folderPushMode"), FolderMode.NONE)
|
||||
|
||||
isSignatureBeforeQuotedText = storage.getBoolean(keyGen.create("signatureBeforeQuotedText"), false)
|
||||
replaceIdentities(loadIdentities(data.id, storage))
|
||||
|
||||
openPgpProvider = storage.getStringOrDefault(keyGen.create("openPgpProvider"), "")
|
||||
openPgpKey = storage.getLong(keyGen.create("cryptoKey"), AccountDefaultsProvider.Companion.NO_OPENPGP_KEY)
|
||||
isOpenPgpHideSignOnly = storage.getBoolean(keyGen.create("openPgpHideSignOnly"), true)
|
||||
isOpenPgpEncryptSubject = storage.getBoolean(keyGen.create("openPgpEncryptSubject"), true)
|
||||
isOpenPgpEncryptAllDrafts = storage.getBoolean(keyGen.create("openPgpEncryptAllDrafts"), true)
|
||||
autocryptPreferEncryptMutual = storage.getBoolean(keyGen.create("autocryptMutualMode"), false)
|
||||
isRemoteSearchFullText = storage.getBoolean(keyGen.create("remoteSearchFullText"), false)
|
||||
remoteSearchNumResults =
|
||||
storage.getInt(
|
||||
keyGen.create("remoteSearchNumResults"),
|
||||
AccountDefaultsProvider.Companion.DEFAULT_REMOTE_SEARCH_NUM_RESULTS,
|
||||
)
|
||||
isUploadSentMessages = storage.getBoolean(keyGen.create("uploadSentMessages"), true)
|
||||
|
||||
isMarkMessageAsReadOnView = storage.getBoolean(keyGen.create("markMessageAsReadOnView"), true)
|
||||
isMarkMessageAsReadOnDelete = storage.getBoolean(keyGen.create("markMessageAsReadOnDelete"), true)
|
||||
isAlwaysShowCcBcc = storage.getBoolean(keyGen.create("alwaysShowCcBcc"), false)
|
||||
lastSyncTime = storage.getLong(keyGen.create("lastSyncTime"), 0L)
|
||||
lastFolderListRefreshTime = storage.getLong(keyGen.create("lastFolderListRefreshTime"), 0L)
|
||||
|
||||
shouldMigrateToOAuth = storage.getBoolean(keyGen.create("migrateToOAuth"), false)
|
||||
folderPathDelimiter = storage.getStringOrDefault(
|
||||
key = keyGen.create(FOLDER_PATH_DELIMITER_KEY),
|
||||
defValue = FOLDER_DEFAULT_PATH_DELIMITER,
|
||||
)
|
||||
|
||||
val isFinishedSetup = storage.getBoolean(keyGen.create("isFinishedSetup"), true)
|
||||
if (isFinishedSetup) markSetupFinished()
|
||||
|
||||
resetChangeMarkers()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun loadIdentities(accountId: AccountId, storage: Storage): List<Identity> {
|
||||
val newIdentities = ArrayList<Identity>()
|
||||
var ident = 0
|
||||
var gotOne: Boolean
|
||||
val keyGen = AccountKeyGenerator(accountId)
|
||||
|
||||
do {
|
||||
gotOne = false
|
||||
val name = storage.getStringOrNull(keyGen.create("$IDENTITY_NAME_KEY.$ident"))
|
||||
val email = storage.getStringOrNull(keyGen.create("$IDENTITY_EMAIL_KEY.$ident"))
|
||||
val signatureUse = storage.getBoolean(keyGen.create("signatureUse.$ident"), false)
|
||||
val signature = storage.getStringOrNull(keyGen.create("signature.$ident"))
|
||||
val description = storage.getStringOrNull(keyGen.create("$IDENTITY_DESCRIPTION_KEY.$ident"))
|
||||
val replyTo = storage.getStringOrNull(keyGen.create("replyTo.$ident"))
|
||||
if (email != null) {
|
||||
val identity = Identity(
|
||||
name = name,
|
||||
email = email,
|
||||
signatureUse = signatureUse,
|
||||
signature = signature,
|
||||
description = description,
|
||||
replyTo = replyTo,
|
||||
)
|
||||
newIdentities.add(identity)
|
||||
gotOne = true
|
||||
}
|
||||
ident++
|
||||
} while (gotOne)
|
||||
|
||||
if (newIdentities.isEmpty()) {
|
||||
val name = storage.getStringOrNull(keyGen.create("name"))
|
||||
val email = storage.getStringOrNull(keyGen.create("email"))
|
||||
val signatureUse = storage.getBoolean(keyGen.create("signatureUse"), false)
|
||||
val signature = storage.getStringOrNull(keyGen.create("signature"))
|
||||
val identity = Identity(
|
||||
name = name,
|
||||
email = email,
|
||||
signatureUse = signatureUse,
|
||||
signature = signature,
|
||||
description = email,
|
||||
)
|
||||
newIdentities.add(identity)
|
||||
}
|
||||
|
||||
return newIdentities
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Synchronized
|
||||
override fun save(data: LegacyAccount, storage: Storage, editor: StorageEditor) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
profileDtoStorageHandler.save(data, storage, editor)
|
||||
|
||||
if (!storage.getStringOrDefault("accountUuids", "").contains(data.uuid)) {
|
||||
var accountUuids = storage.getStringOrDefault("accountUuids", "")
|
||||
accountUuids += (if (accountUuids.isNotEmpty()) "," else "") + data.uuid
|
||||
editor.putString("accountUuids", accountUuids)
|
||||
}
|
||||
|
||||
with(data) {
|
||||
editor.putString(
|
||||
keyGen.create(INCOMING_SERVER_SETTINGS_KEY),
|
||||
serverSettingsDtoSerializer.serialize(incomingServerSettings),
|
||||
)
|
||||
editor.putString(
|
||||
keyGen.create(OUTGOING_SERVER_SETTINGS_KEY),
|
||||
serverSettingsDtoSerializer.serialize(outgoingServerSettings),
|
||||
)
|
||||
editor.putString(keyGen.create("oAuthState"), oAuthState)
|
||||
editor.putString(keyGen.create("alwaysBcc"), alwaysBcc)
|
||||
editor.putInt(keyGen.create("automaticCheckIntervalMinutes"), automaticCheckIntervalMinutes)
|
||||
editor.putInt(keyGen.create("idleRefreshMinutes"), idleRefreshMinutes)
|
||||
editor.putInt(keyGen.create("displayCount"), displayCount)
|
||||
editor.putBoolean(keyGen.create("notifyNewMail"), isNotifyNewMail)
|
||||
editor.putString(keyGen.create("folderNotifyNewMailMode"), folderNotifyNewMailMode.name)
|
||||
editor.putBoolean(keyGen.create("notifySelfNewMail"), isNotifySelfNewMail)
|
||||
editor.putBoolean(keyGen.create("notifyContactsMailOnly"), isNotifyContactsMailOnly)
|
||||
editor.putBoolean(keyGen.create("ignoreChatMessages"), isIgnoreChatMessages)
|
||||
editor.putBoolean(keyGen.create("notifyMailCheck"), isNotifySync)
|
||||
editor.putInt(keyGen.create("messagesNotificationChannelVersion"), messagesNotificationChannelVersion)
|
||||
editor.putInt(keyGen.create("deletePolicy"), deletePolicy.setting)
|
||||
editor.putString(keyGen.create("inboxFolderName"), legacyInboxFolder)
|
||||
editor.putString(keyGen.create("draftsFolderName"), importedDraftsFolder)
|
||||
editor.putString(keyGen.create("sentFolderName"), importedSentFolder)
|
||||
editor.putString(keyGen.create("trashFolderName"), importedTrashFolder)
|
||||
editor.putString(keyGen.create("archiveFolderName"), importedArchiveFolder)
|
||||
editor.putString(keyGen.create("spamFolderName"), importedSpamFolder)
|
||||
editor.putString(keyGen.create("inboxFolderId"), inboxFolderId?.toString())
|
||||
editor.putString(keyGen.create("draftsFolderId"), draftsFolderId?.toString())
|
||||
editor.putString(keyGen.create("sentFolderId"), sentFolderId?.toString())
|
||||
editor.putString(keyGen.create("trashFolderId"), trashFolderId?.toString())
|
||||
editor.putString(keyGen.create("archiveFolderId"), archiveFolderId?.toString())
|
||||
editor.putString(keyGen.create("spamFolderId"), spamFolderId?.toString())
|
||||
editor.putString(keyGen.create("archiveFolderSelection"), archiveFolderSelection.name)
|
||||
editor.putString(keyGen.create("draftsFolderSelection"), draftsFolderSelection.name)
|
||||
editor.putString(keyGen.create("sentFolderSelection"), sentFolderSelection.name)
|
||||
editor.putString(keyGen.create("spamFolderSelection"), spamFolderSelection.name)
|
||||
editor.putString(keyGen.create("trashFolderSelection"), trashFolderSelection.name)
|
||||
editor.putString(keyGen.create("autoExpandFolderName"), importedAutoExpandFolder)
|
||||
editor.putString(keyGen.create("autoExpandFolderId"), autoExpandFolderId?.toString())
|
||||
editor.putInt(keyGen.create("accountNumber"), accountNumber)
|
||||
editor.putString(keyGen.create("sortTypeEnum"), sortType.name)
|
||||
editor.putBoolean(keyGen.create("sortAscending"), isSortAscending(sortType))
|
||||
editor.putString(keyGen.create("showPicturesEnum"), showPictures.name)
|
||||
editor.putString(keyGen.create("folderDisplayMode"), folderDisplayMode.name)
|
||||
editor.putString(keyGen.create("folderSyncMode"), folderSyncMode.name)
|
||||
editor.putString(keyGen.create("folderPushMode"), folderPushMode.name)
|
||||
editor.putBoolean(keyGen.create("signatureBeforeQuotedText"), isSignatureBeforeQuotedText)
|
||||
editor.putString(keyGen.create("expungePolicy"), expungePolicy.name)
|
||||
editor.putBoolean(keyGen.create("syncRemoteDeletions"), isSyncRemoteDeletions)
|
||||
editor.putInt(keyGen.create("maxPushFolders"), maxPushFolders)
|
||||
editor.putBoolean(keyGen.create("subscribedFoldersOnly"), isSubscribedFoldersOnly)
|
||||
editor.putInt(keyGen.create("maximumPolledMessageAge"), maximumPolledMessageAge)
|
||||
editor.putInt(keyGen.create("maximumAutoDownloadMessageSize"), maximumAutoDownloadMessageSize)
|
||||
val messageFormatAuto = if (MessageFormat.AUTO == messageFormat) {
|
||||
// saving MessageFormat.AUTO as is to the database will cause downgrades to crash on
|
||||
// startup, so we save as MessageFormat.TEXT instead with a separate flag for auto.
|
||||
editor.putString(keyGen.create("messageFormat"), MessageFormat.TEXT.name)
|
||||
true
|
||||
} else {
|
||||
editor.putString(keyGen.create("messageFormat"), messageFormat.name)
|
||||
false
|
||||
}
|
||||
editor.putBoolean(keyGen.create("messageFormatAuto"), messageFormatAuto)
|
||||
editor.putBoolean(keyGen.create("messageReadReceipt"), isMessageReadReceipt)
|
||||
editor.putString(keyGen.create("quoteStyle"), quoteStyle.name)
|
||||
editor.putString(keyGen.create("quotePrefix"), quotePrefix)
|
||||
editor.putBoolean(keyGen.create("defaultQuotedTextShown"), isDefaultQuotedTextShown)
|
||||
editor.putBoolean(keyGen.create("replyAfterQuote"), isReplyAfterQuote)
|
||||
editor.putBoolean(keyGen.create("stripSignature"), isStripSignature)
|
||||
editor.putLong(keyGen.create("cryptoKey"), openPgpKey)
|
||||
editor.putBoolean(keyGen.create("openPgpHideSignOnly"), isOpenPgpHideSignOnly)
|
||||
editor.putBoolean(keyGen.create("openPgpEncryptSubject"), isOpenPgpEncryptSubject)
|
||||
editor.putBoolean(keyGen.create("openPgpEncryptAllDrafts"), isOpenPgpEncryptAllDrafts)
|
||||
editor.putString(keyGen.create("openPgpProvider"), openPgpProvider)
|
||||
editor.putBoolean(keyGen.create("autocryptMutualMode"), autocryptPreferEncryptMutual)
|
||||
editor.putBoolean(keyGen.create("remoteSearchFullText"), isRemoteSearchFullText)
|
||||
editor.putInt(keyGen.create("remoteSearchNumResults"), remoteSearchNumResults)
|
||||
editor.putBoolean(keyGen.create("uploadSentMessages"), isUploadSentMessages)
|
||||
editor.putBoolean(keyGen.create("markMessageAsReadOnView"), isMarkMessageAsReadOnView)
|
||||
editor.putBoolean(keyGen.create("markMessageAsReadOnDelete"), isMarkMessageAsReadOnDelete)
|
||||
editor.putBoolean(keyGen.create("alwaysShowCcBcc"), isAlwaysShowCcBcc)
|
||||
|
||||
editor.putBoolean(keyGen.create("vibrate"), notificationSettings.vibration.isEnabled)
|
||||
editor.putInt(keyGen.create("vibratePattern"), notificationSettings.vibration.pattern.serialize())
|
||||
editor.putInt(keyGen.create("vibrateTimes"), notificationSettings.vibration.repeatCount)
|
||||
editor.putBoolean(keyGen.create("ring"), notificationSettings.isRingEnabled)
|
||||
editor.putString(keyGen.create("ringtone"), notificationSettings.ringtone)
|
||||
editor.putString(keyGen.create("notificationLight"), notificationSettings.light.name)
|
||||
editor.putLong(keyGen.create("lastSyncTime"), lastSyncTime)
|
||||
editor.putLong(keyGen.create("lastFolderListRefreshTime"), lastFolderListRefreshTime)
|
||||
editor.putBoolean(keyGen.create("isFinishedSetup"), isFinishedSetup)
|
||||
editor.putBoolean(keyGen.create("useCompression"), useCompression)
|
||||
editor.putBoolean(keyGen.create("sendClientInfo"), isSendClientInfoEnabled)
|
||||
editor.putBoolean(keyGen.create("migrateToOAuth"), shouldMigrateToOAuth)
|
||||
editor.putString(keyGen.create(FOLDER_PATH_DELIMITER_KEY), folderPathDelimiter)
|
||||
}
|
||||
|
||||
saveIdentities(data, storage, editor)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Synchronized
|
||||
override fun delete(data: LegacyAccount, storage: Storage, editor: StorageEditor) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
val accountUuid = data.uuid
|
||||
|
||||
profileDtoStorageHandler.delete(data, storage, editor)
|
||||
|
||||
// Get the list of account UUIDs
|
||||
val uuids = storage
|
||||
.getStringOrDefault("accountUuids", "")
|
||||
.split(",".toRegex())
|
||||
.dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
|
||||
// Create a list of all account UUIDs excluding this account
|
||||
val newUuids = ArrayList<String>(uuids.size)
|
||||
for (uuid in uuids) {
|
||||
if (uuid != accountUuid) {
|
||||
newUuids.add(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
// Only change the 'accountUuids' value if this account's UUID was listed before
|
||||
if (newUuids.size < uuids.size) {
|
||||
val accountUuids = newUuids.joinToString(",")
|
||||
editor.putString("accountUuids", accountUuids)
|
||||
}
|
||||
|
||||
editor.remove(keyGen.create("oAuthState"))
|
||||
editor.remove(keyGen.create(INCOMING_SERVER_SETTINGS_KEY))
|
||||
editor.remove(keyGen.create(OUTGOING_SERVER_SETTINGS_KEY))
|
||||
editor.remove(keyGen.create("description"))
|
||||
editor.remove(keyGen.create("email"))
|
||||
editor.remove(keyGen.create("alwaysBcc"))
|
||||
editor.remove(keyGen.create("automaticCheckIntervalMinutes"))
|
||||
editor.remove(keyGen.create("idleRefreshMinutes"))
|
||||
editor.remove(keyGen.create("lastAutomaticCheckTime"))
|
||||
editor.remove(keyGen.create("notifyNewMail"))
|
||||
editor.remove(keyGen.create("notifySelfNewMail"))
|
||||
editor.remove(keyGen.create("ignoreChatMessages"))
|
||||
editor.remove(keyGen.create("messagesNotificationChannelVersion"))
|
||||
editor.remove(keyGen.create("deletePolicy"))
|
||||
editor.remove(keyGen.create("draftsFolderName"))
|
||||
editor.remove(keyGen.create("sentFolderName"))
|
||||
editor.remove(keyGen.create("trashFolderName"))
|
||||
editor.remove(keyGen.create("archiveFolderName"))
|
||||
editor.remove(keyGen.create("spamFolderName"))
|
||||
editor.remove(keyGen.create("archiveFolderSelection"))
|
||||
editor.remove(keyGen.create("draftsFolderSelection"))
|
||||
editor.remove(keyGen.create("sentFolderSelection"))
|
||||
editor.remove(keyGen.create("spamFolderSelection"))
|
||||
editor.remove(keyGen.create("trashFolderSelection"))
|
||||
editor.remove(keyGen.create("autoExpandFolderName"))
|
||||
editor.remove(keyGen.create("accountNumber"))
|
||||
editor.remove(keyGen.create("vibrate"))
|
||||
editor.remove(keyGen.create("vibratePattern"))
|
||||
editor.remove(keyGen.create("vibrateTimes"))
|
||||
editor.remove(keyGen.create("ring"))
|
||||
editor.remove(keyGen.create("ringtone"))
|
||||
editor.remove(keyGen.create("folderDisplayMode"))
|
||||
editor.remove(keyGen.create("folderSyncMode"))
|
||||
editor.remove(keyGen.create("folderPushMode"))
|
||||
editor.remove(keyGen.create("signatureBeforeQuotedText"))
|
||||
editor.remove(keyGen.create("expungePolicy"))
|
||||
editor.remove(keyGen.create("syncRemoteDeletions"))
|
||||
editor.remove(keyGen.create("maxPushFolders"))
|
||||
editor.remove(keyGen.create("notificationLight"))
|
||||
editor.remove(keyGen.create("subscribedFoldersOnly"))
|
||||
editor.remove(keyGen.create("maximumPolledMessageAge"))
|
||||
editor.remove(keyGen.create("maximumAutoDownloadMessageSize"))
|
||||
editor.remove(keyGen.create("messageFormatAuto"))
|
||||
editor.remove(keyGen.create("quoteStyle"))
|
||||
editor.remove(keyGen.create("quotePrefix"))
|
||||
editor.remove(keyGen.create("sortTypeEnum"))
|
||||
editor.remove(keyGen.create("sortAscending"))
|
||||
editor.remove(keyGen.create("showPicturesEnum"))
|
||||
editor.remove(keyGen.create("replyAfterQuote"))
|
||||
editor.remove(keyGen.create("stripSignature"))
|
||||
editor.remove(keyGen.create("cryptoApp")) // this is no longer set, but cleans up legacy values
|
||||
editor.remove(keyGen.create("cryptoAutoSignature"))
|
||||
editor.remove(keyGen.create("cryptoAutoEncrypt"))
|
||||
editor.remove(keyGen.create("cryptoApp"))
|
||||
editor.remove(keyGen.create("cryptoKey"))
|
||||
editor.remove(keyGen.create("cryptoSupportSignOnly"))
|
||||
editor.remove(keyGen.create("openPgpProvider"))
|
||||
editor.remove(keyGen.create("openPgpHideSignOnly"))
|
||||
editor.remove(keyGen.create("openPgpEncryptSubject"))
|
||||
editor.remove(keyGen.create("openPgpEncryptAllDrafts"))
|
||||
editor.remove(keyGen.create("autocryptMutualMode"))
|
||||
editor.remove(keyGen.create("enabled"))
|
||||
editor.remove(keyGen.create("markMessageAsReadOnView"))
|
||||
editor.remove(keyGen.create("markMessageAsReadOnDelete"))
|
||||
editor.remove(keyGen.create("alwaysShowCcBcc"))
|
||||
editor.remove(keyGen.create("remoteSearchFullText"))
|
||||
editor.remove(keyGen.create("remoteSearchNumResults"))
|
||||
editor.remove(keyGen.create("uploadSentMessages"))
|
||||
editor.remove(keyGen.create("defaultQuotedTextShown"))
|
||||
editor.remove(keyGen.create("displayCount"))
|
||||
editor.remove(keyGen.create("inboxFolderName"))
|
||||
editor.remove(keyGen.create("messageFormat"))
|
||||
editor.remove(keyGen.create("messageReadReceipt"))
|
||||
editor.remove(keyGen.create("notifyMailCheck"))
|
||||
editor.remove(keyGen.create("inboxFolderId"))
|
||||
editor.remove(keyGen.create("outboxFolderId"))
|
||||
editor.remove(keyGen.create("draftsFolderId"))
|
||||
editor.remove(keyGen.create("sentFolderId"))
|
||||
editor.remove(keyGen.create("trashFolderId"))
|
||||
editor.remove(keyGen.create("archiveFolderId"))
|
||||
editor.remove(keyGen.create("spamFolderId"))
|
||||
editor.remove(keyGen.create("autoExpandFolderId"))
|
||||
editor.remove(keyGen.create("lastSyncTime"))
|
||||
editor.remove(keyGen.create("lastFolderListRefreshTime"))
|
||||
editor.remove(keyGen.create("isFinishedSetup"))
|
||||
editor.remove(keyGen.create("useCompression"))
|
||||
editor.remove(keyGen.create("sendClientInfo"))
|
||||
editor.remove(keyGen.create("migrateToOAuth"))
|
||||
editor.remove(keyGen.create(FOLDER_PATH_DELIMITER_KEY))
|
||||
|
||||
deleteIdentities(data, storage, editor)
|
||||
// TODO: Remove preference settings that may exist for individual folders in the account.
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun saveIdentities(data: LegacyAccount, storage: Storage, editor: StorageEditor) {
|
||||
deleteIdentities(data, storage, editor)
|
||||
var ident = 0
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
with(data) {
|
||||
for (identity in identities) {
|
||||
editor.putString(keyGen.create("$IDENTITY_NAME_KEY.$ident"), identity.name)
|
||||
editor.putString(keyGen.create("$IDENTITY_EMAIL_KEY.$ident"), identity.email)
|
||||
editor.putBoolean(keyGen.create("signatureUse.$ident"), identity.signatureUse)
|
||||
editor.putString(keyGen.create("signature.$ident"), identity.signature)
|
||||
editor.putString(keyGen.create("$IDENTITY_DESCRIPTION_KEY.$ident"), identity.description)
|
||||
editor.putString(keyGen.create("replyTo.$ident"), identity.replyTo)
|
||||
ident++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun deleteIdentities(data: LegacyAccount, storage: Storage, editor: StorageEditor) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
var identityIndex = 0
|
||||
var gotOne: Boolean
|
||||
do {
|
||||
gotOne = false
|
||||
val email = storage.getStringOrNull(keyGen.create("$IDENTITY_EMAIL_KEY.$identityIndex"))
|
||||
if (email != null) {
|
||||
editor.remove(keyGen.create("$IDENTITY_NAME_KEY.$identityIndex"))
|
||||
editor.remove(keyGen.create("$IDENTITY_EMAIL_KEY.$identityIndex"))
|
||||
editor.remove(keyGen.create("signatureUse.$identityIndex"))
|
||||
editor.remove(keyGen.create("signature.$identityIndex"))
|
||||
editor.remove(keyGen.create("$IDENTITY_DESCRIPTION_KEY.$identityIndex"))
|
||||
editor.remove(keyGen.create("replyTo.$identityIndex"))
|
||||
gotOne = true
|
||||
}
|
||||
identityIndex++
|
||||
} while (gotOne)
|
||||
}
|
||||
|
||||
private inline fun <reified T : Enum<T>> getEnumStringPref(storage: Storage, key: String, defaultEnum: T): T {
|
||||
return try {
|
||||
storage.getEnumOrDefault<T>(key, defaultEnum)
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
logger.warn(throwable = ex) {
|
||||
"Unable to convert preference key [$key] to enum of type defaultEnum: $defaultEnum"
|
||||
}
|
||||
|
||||
defaultEnum
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACCOUNT_DESCRIPTION_KEY = "description"
|
||||
const val INCOMING_SERVER_SETTINGS_KEY = "incomingServerSettings"
|
||||
const val OUTGOING_SERVER_SETTINGS_KEY = "outgoingServerSettings"
|
||||
|
||||
const val IDENTITY_NAME_KEY = "name"
|
||||
const val IDENTITY_EMAIL_KEY = "email"
|
||||
const val IDENTITY_DESCRIPTION_KEY = "description"
|
||||
|
||||
const val FOLDER_PATH_DELIMITER_KEY = "folderPathDelimiter"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.preference.storage.Storage
|
||||
import net.thunderbird.core.preference.storage.StorageEditor
|
||||
import net.thunderbird.core.preference.storage.getEnumOrDefault
|
||||
import net.thunderbird.core.preference.storage.putEnum
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
|
||||
class LegacyAvatarDtoStorageHandler : AvatarDtoStorageHandler {
|
||||
|
||||
override fun load(
|
||||
data: LegacyAccount,
|
||||
storage: Storage,
|
||||
) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
with(data) {
|
||||
avatar = AvatarDto(
|
||||
avatarType = storage.getEnumOrDefault(keyGen.create(KEY_AVATAR_TYPE), AvatarTypeDto.MONOGRAM),
|
||||
avatarMonogram = storage.getStringOrNull(keyGen.create(KEY_AVATAR_MONOGRAM)),
|
||||
avatarImageUri = storage.getStringOrNull(keyGen.create(KEY_AVATAR_IMAGE_URI)),
|
||||
avatarIconName = storage.getStringOrNull(keyGen.create(KEY_AVATAR_ICON_NAME)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(
|
||||
data: LegacyAccount,
|
||||
storage: Storage,
|
||||
editor: StorageEditor,
|
||||
) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
with(data.avatar) {
|
||||
editor.putEnum(keyGen.create(KEY_AVATAR_TYPE), avatarType)
|
||||
editor.putString(keyGen.create(KEY_AVATAR_MONOGRAM), avatarMonogram)
|
||||
editor.putString(keyGen.create(KEY_AVATAR_IMAGE_URI), avatarImageUri)
|
||||
editor.putString(keyGen.create(KEY_AVATAR_ICON_NAME), avatarIconName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(
|
||||
data: LegacyAccount,
|
||||
storage: Storage,
|
||||
editor: StorageEditor,
|
||||
) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
editor.remove(keyGen.create(KEY_AVATAR_TYPE))
|
||||
editor.remove(keyGen.create(KEY_AVATAR_MONOGRAM))
|
||||
editor.remove(keyGen.create(KEY_AVATAR_IMAGE_URI))
|
||||
editor.remove(keyGen.create(KEY_AVATAR_ICON_NAME))
|
||||
}
|
||||
|
||||
private companion object Companion {
|
||||
const val KEY_AVATAR_TYPE = "avatarType"
|
||||
const val KEY_AVATAR_MONOGRAM = "avatarMonogram"
|
||||
const val KEY_AVATAR_IMAGE_URI = "avatarImageUri"
|
||||
const val KEY_AVATAR_ICON_NAME = "avatarIconName"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import net.thunderbird.core.android.account.AccountDefaultsProvider
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.preference.storage.Storage
|
||||
import net.thunderbird.core.preference.storage.StorageEditor
|
||||
|
||||
class LegacyProfileDtoStorageHandler(
|
||||
private val avatarDtoStorageHandler: AvatarDtoStorageHandler,
|
||||
) : ProfileDtoStorageHandler {
|
||||
|
||||
override fun load(
|
||||
data: LegacyAccount,
|
||||
storage: Storage,
|
||||
) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
with(data) {
|
||||
name = storage.getStringOrNull(keyGen.create(KEY_NAME))
|
||||
chipColor = storage.getInt(keyGen.create(KEY_COLOR), AccountDefaultsProvider.COLOR)
|
||||
}
|
||||
|
||||
avatarDtoStorageHandler.load(data, storage)
|
||||
}
|
||||
|
||||
override fun save(
|
||||
data: LegacyAccount,
|
||||
storage: Storage,
|
||||
editor: StorageEditor,
|
||||
) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
with(data) {
|
||||
editor.putString(keyGen.create(KEY_NAME), name)
|
||||
editor.putInt(keyGen.create(KEY_COLOR), chipColor)
|
||||
}
|
||||
|
||||
avatarDtoStorageHandler.save(data, storage, editor)
|
||||
}
|
||||
|
||||
override fun delete(
|
||||
data: LegacyAccount,
|
||||
storage: Storage,
|
||||
editor: StorageEditor,
|
||||
) {
|
||||
val keyGen = AccountKeyGenerator(data.id)
|
||||
|
||||
editor.remove(keyGen.create(KEY_NAME))
|
||||
editor.remove(keyGen.create(KEY_COLOR))
|
||||
|
||||
avatarDtoStorageHandler.delete(data, storage, editor)
|
||||
}
|
||||
|
||||
private companion object Companion {
|
||||
const val KEY_COLOR = "chipColor"
|
||||
const val KEY_NAME = "description"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import androidx.annotation.Discouraged
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.preference.storage.Storage
|
||||
import net.thunderbird.core.preference.storage.StorageEditor
|
||||
|
||||
/**
|
||||
* Represents a storage handler for a specific data type.
|
||||
*
|
||||
* @param T The type of data that this handler can handle.
|
||||
*/
|
||||
@Discouraged(
|
||||
message = "This interface is only used to encapsulate the [LegacyAccount] storage handling.",
|
||||
)
|
||||
interface StorageHandler<T> {
|
||||
|
||||
/**
|
||||
* Loads the data from the storage into the provided object.
|
||||
*
|
||||
* @param data The object to load the data into.
|
||||
* @param storage The storage to load the data from.
|
||||
*/
|
||||
fun load(data: T, storage: Storage)
|
||||
|
||||
/**
|
||||
* Saves the data from the provided object to the storage.
|
||||
*
|
||||
* @param data The object to save the data from.
|
||||
* @param storage The storage to save the data to.
|
||||
* @param editor The storage editor to use for saving the data.
|
||||
*/
|
||||
fun save(data: T, storage: Storage, editor: StorageEditor)
|
||||
|
||||
/**
|
||||
* Deletes the data from the storage.
|
||||
*
|
||||
* @param data The data to delete.
|
||||
* @param storage The storage to delete the data from.
|
||||
* @param editor The storage editor to use for deleting the data.
|
||||
*/
|
||||
fun delete(data: T, storage: Storage, editor: StorageEditor)
|
||||
}
|
||||
|
||||
interface AccountDtoStorageHandler : StorageHandler<LegacyAccount>
|
||||
|
||||
interface ProfileDtoStorageHandler : StorageHandler<LegacyAccount>
|
||||
|
||||
interface AvatarDtoStorageHandler : StorageHandler<LegacyAccount>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import net.thunderbird.feature.account.profile.AccountAvatar
|
||||
import net.thunderbird.feature.account.storage.mapper.AccountAvatarDataMapper
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
|
||||
class DefaultAccountAvatarDataMapper : AccountAvatarDataMapper {
|
||||
|
||||
override fun toDomain(dto: AvatarDto): AccountAvatar {
|
||||
return when (dto.avatarType) {
|
||||
AvatarTypeDto.MONOGRAM -> AccountAvatar.Monogram(
|
||||
value = dto.avatarMonogram ?: DEFAULT_MONOGRAM,
|
||||
)
|
||||
|
||||
AvatarTypeDto.IMAGE -> {
|
||||
val uri = dto.avatarImageUri
|
||||
|
||||
if (uri.isNullOrEmpty()) {
|
||||
AccountAvatar.Monogram(
|
||||
value = DEFAULT_MONOGRAM,
|
||||
)
|
||||
} else {
|
||||
AccountAvatar.Image(
|
||||
uri = uri,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AvatarTypeDto.ICON -> {
|
||||
val name = dto.avatarIconName
|
||||
|
||||
if (name.isNullOrEmpty()) {
|
||||
AccountAvatar.Monogram(
|
||||
value = DEFAULT_MONOGRAM,
|
||||
)
|
||||
} else {
|
||||
AccountAvatar.Icon(
|
||||
name = name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toDto(domain: AccountAvatar): AvatarDto {
|
||||
return AvatarDto(
|
||||
avatarType = when (domain) {
|
||||
is AccountAvatar.Monogram -> AvatarTypeDto.MONOGRAM
|
||||
is AccountAvatar.Image -> AvatarTypeDto.IMAGE
|
||||
is AccountAvatar.Icon -> AvatarTypeDto.ICON
|
||||
},
|
||||
avatarMonogram = if (domain is AccountAvatar.Monogram) domain.value else null,
|
||||
avatarImageUri = if (domain is AccountAvatar.Image) domain.uri else null,
|
||||
avatarIconName = if (domain is AccountAvatar.Icon) domain.name else null,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DEFAULT_MONOGRAM = "XX"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import net.thunderbird.feature.account.profile.AccountProfile
|
||||
import net.thunderbird.feature.account.storage.mapper.AccountAvatarDataMapper
|
||||
import net.thunderbird.feature.account.storage.mapper.AccountProfileDataMapper
|
||||
import net.thunderbird.feature.account.storage.profile.ProfileDto
|
||||
|
||||
class DefaultAccountProfileDataMapper(
|
||||
private val avatarMapper: AccountAvatarDataMapper,
|
||||
) : AccountProfileDataMapper {
|
||||
override fun toDomain(dto: ProfileDto): AccountProfile {
|
||||
return AccountProfile(
|
||||
id = dto.id,
|
||||
name = dto.name,
|
||||
color = dto.color,
|
||||
avatar = avatarMapper.toDomain(dto.avatar),
|
||||
)
|
||||
}
|
||||
|
||||
override fun toDto(domain: AccountProfile): ProfileDto {
|
||||
return ProfileDto(
|
||||
id = domain.id,
|
||||
name = domain.name,
|
||||
color = domain.color,
|
||||
avatar = avatarMapper.toDto(domain.avatar),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.android.account.LegacyAccountWrapper
|
||||
import net.thunderbird.core.architecture.data.DataMapper
|
||||
import net.thunderbird.feature.account.storage.profile.ProfileDto
|
||||
|
||||
class DefaultLegacyAccountWrapperDataMapper : DataMapper<LegacyAccountWrapper, LegacyAccount> {
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun toDomain(dto: LegacyAccount): LegacyAccountWrapper {
|
||||
return LegacyAccountWrapper(
|
||||
isSensitiveDebugLoggingEnabled = dto.isSensitiveDebugLoggingEnabled,
|
||||
|
||||
// Account
|
||||
id = dto.id,
|
||||
|
||||
// BaseAccount
|
||||
name = dto.name,
|
||||
|
||||
// AccountProfile
|
||||
profile = toProfileDto(dto),
|
||||
|
||||
// Uncategorized
|
||||
identities = dto.identities,
|
||||
email = dto.email,
|
||||
deletePolicy = dto.deletePolicy,
|
||||
incomingServerSettings = dto.incomingServerSettings,
|
||||
outgoingServerSettings = dto.outgoingServerSettings,
|
||||
oAuthState = dto.oAuthState,
|
||||
alwaysBcc = dto.alwaysBcc,
|
||||
automaticCheckIntervalMinutes = dto.automaticCheckIntervalMinutes,
|
||||
displayCount = dto.displayCount,
|
||||
isNotifyNewMail = dto.isNotifyNewMail,
|
||||
folderNotifyNewMailMode = dto.folderNotifyNewMailMode,
|
||||
isNotifySelfNewMail = dto.isNotifySelfNewMail,
|
||||
isNotifyContactsMailOnly = dto.isNotifyContactsMailOnly,
|
||||
isIgnoreChatMessages = dto.isIgnoreChatMessages,
|
||||
legacyInboxFolder = dto.legacyInboxFolder,
|
||||
importedDraftsFolder = dto.importedDraftsFolder,
|
||||
importedSentFolder = dto.importedSentFolder,
|
||||
importedTrashFolder = dto.importedTrashFolder,
|
||||
importedArchiveFolder = dto.importedArchiveFolder,
|
||||
importedSpamFolder = dto.importedSpamFolder,
|
||||
inboxFolderId = dto.inboxFolderId,
|
||||
draftsFolderId = dto.draftsFolderId,
|
||||
sentFolderId = dto.sentFolderId,
|
||||
trashFolderId = dto.trashFolderId,
|
||||
archiveFolderId = dto.archiveFolderId,
|
||||
spamFolderId = dto.spamFolderId,
|
||||
draftsFolderSelection = dto.draftsFolderSelection,
|
||||
sentFolderSelection = dto.sentFolderSelection,
|
||||
trashFolderSelection = dto.trashFolderSelection,
|
||||
archiveFolderSelection = dto.archiveFolderSelection,
|
||||
spamFolderSelection = dto.spamFolderSelection,
|
||||
importedAutoExpandFolder = dto.importedAutoExpandFolder,
|
||||
autoExpandFolderId = dto.autoExpandFolderId,
|
||||
folderDisplayMode = dto.folderDisplayMode,
|
||||
folderSyncMode = dto.folderSyncMode,
|
||||
folderPushMode = dto.folderPushMode,
|
||||
accountNumber = dto.accountNumber,
|
||||
isNotifySync = dto.isNotifySync,
|
||||
sortType = dto.sortType,
|
||||
sortAscending = dto.sortAscending,
|
||||
showPictures = dto.showPictures,
|
||||
isSignatureBeforeQuotedText = dto.isSignatureBeforeQuotedText,
|
||||
expungePolicy = dto.expungePolicy,
|
||||
maxPushFolders = dto.maxPushFolders,
|
||||
idleRefreshMinutes = dto.idleRefreshMinutes,
|
||||
useCompression = dto.useCompression,
|
||||
isSendClientInfoEnabled = dto.isSendClientInfoEnabled,
|
||||
isSubscribedFoldersOnly = dto.isSubscribedFoldersOnly,
|
||||
maximumPolledMessageAge = dto.maximumPolledMessageAge,
|
||||
maximumAutoDownloadMessageSize = dto.maximumAutoDownloadMessageSize,
|
||||
messageFormat = dto.messageFormat,
|
||||
isMessageFormatAuto = dto.isMessageFormatAuto,
|
||||
isMessageReadReceipt = dto.isMessageReadReceipt,
|
||||
quoteStyle = dto.quoteStyle,
|
||||
quotePrefix = dto.quotePrefix,
|
||||
isDefaultQuotedTextShown = dto.isDefaultQuotedTextShown,
|
||||
isReplyAfterQuote = dto.isReplyAfterQuote,
|
||||
isStripSignature = dto.isStripSignature,
|
||||
isSyncRemoteDeletions = dto.isSyncRemoteDeletions,
|
||||
openPgpProvider = dto.openPgpProvider,
|
||||
openPgpKey = dto.openPgpKey,
|
||||
autocryptPreferEncryptMutual = dto.autocryptPreferEncryptMutual,
|
||||
isOpenPgpHideSignOnly = dto.isOpenPgpHideSignOnly,
|
||||
isOpenPgpEncryptSubject = dto.isOpenPgpEncryptSubject,
|
||||
isOpenPgpEncryptAllDrafts = dto.isOpenPgpEncryptAllDrafts,
|
||||
isMarkMessageAsReadOnView = dto.isMarkMessageAsReadOnView,
|
||||
isMarkMessageAsReadOnDelete = dto.isMarkMessageAsReadOnDelete,
|
||||
isAlwaysShowCcBcc = dto.isAlwaysShowCcBcc,
|
||||
isRemoteSearchFullText = dto.isRemoteSearchFullText,
|
||||
remoteSearchNumResults = dto.remoteSearchNumResults,
|
||||
isUploadSentMessages = dto.isUploadSentMessages,
|
||||
lastSyncTime = dto.lastSyncTime,
|
||||
lastFolderListRefreshTime = dto.lastFolderListRefreshTime,
|
||||
isFinishedSetup = dto.isFinishedSetup,
|
||||
messagesNotificationChannelVersion = dto.messagesNotificationChannelVersion,
|
||||
isChangedVisibleLimits = dto.isChangedVisibleLimits,
|
||||
lastSelectedFolderId = dto.lastSelectedFolderId,
|
||||
notificationSettings = dto.notificationSettings,
|
||||
senderName = dto.senderName,
|
||||
signatureUse = dto.signatureUse,
|
||||
signature = dto.signature,
|
||||
shouldMigrateToOAuth = dto.shouldMigrateToOAuth,
|
||||
folderPathDelimiter = dto.folderPathDelimiter,
|
||||
)
|
||||
}
|
||||
|
||||
private fun toProfileDto(dto: LegacyAccount): ProfileDto {
|
||||
return ProfileDto(
|
||||
id = dto.id,
|
||||
name = dto.displayName,
|
||||
color = dto.chipColor,
|
||||
avatar = dto.avatar,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun toDto(domain: LegacyAccountWrapper): LegacyAccount {
|
||||
return LegacyAccount(
|
||||
uuid = domain.uuid,
|
||||
isSensitiveDebugLoggingEnabled = domain.isSensitiveDebugLoggingEnabled,
|
||||
).apply {
|
||||
identities = domain.identities.toMutableList()
|
||||
email = domain.email
|
||||
|
||||
// [AccountProfile]
|
||||
fromProfileDto(domain.profile, this)
|
||||
|
||||
// Uncategorized
|
||||
deletePolicy = domain.deletePolicy
|
||||
incomingServerSettings = domain.incomingServerSettings
|
||||
outgoingServerSettings = domain.outgoingServerSettings
|
||||
oAuthState = domain.oAuthState
|
||||
alwaysBcc = domain.alwaysBcc
|
||||
automaticCheckIntervalMinutes = domain.automaticCheckIntervalMinutes
|
||||
displayCount = domain.displayCount
|
||||
isNotifyNewMail = domain.isNotifyNewMail
|
||||
folderNotifyNewMailMode = domain.folderNotifyNewMailMode
|
||||
isNotifySelfNewMail = domain.isNotifySelfNewMail
|
||||
isNotifyContactsMailOnly = domain.isNotifyContactsMailOnly
|
||||
isIgnoreChatMessages = domain.isIgnoreChatMessages
|
||||
legacyInboxFolder = domain.legacyInboxFolder
|
||||
importedDraftsFolder = domain.importedDraftsFolder
|
||||
importedSentFolder = domain.importedSentFolder
|
||||
importedTrashFolder = domain.importedTrashFolder
|
||||
importedArchiveFolder = domain.importedArchiveFolder
|
||||
importedSpamFolder = domain.importedSpamFolder
|
||||
inboxFolderId = domain.inboxFolderId
|
||||
draftsFolderId = domain.draftsFolderId
|
||||
sentFolderId = domain.sentFolderId
|
||||
trashFolderId = domain.trashFolderId
|
||||
archiveFolderId = domain.archiveFolderId
|
||||
spamFolderId = domain.spamFolderId
|
||||
draftsFolderSelection = domain.draftsFolderSelection
|
||||
sentFolderSelection = domain.sentFolderSelection
|
||||
trashFolderSelection = domain.trashFolderSelection
|
||||
archiveFolderSelection = domain.archiveFolderSelection
|
||||
spamFolderSelection = domain.spamFolderSelection
|
||||
importedAutoExpandFolder = domain.importedAutoExpandFolder
|
||||
autoExpandFolderId = domain.autoExpandFolderId
|
||||
folderDisplayMode = domain.folderDisplayMode
|
||||
folderSyncMode = domain.folderSyncMode
|
||||
folderPushMode = domain.folderPushMode
|
||||
accountNumber = domain.accountNumber
|
||||
isNotifySync = domain.isNotifySync
|
||||
sortType = domain.sortType
|
||||
sortAscending = domain.sortAscending.toMutableMap()
|
||||
showPictures = domain.showPictures
|
||||
isSignatureBeforeQuotedText = domain.isSignatureBeforeQuotedText
|
||||
expungePolicy = domain.expungePolicy
|
||||
maxPushFolders = domain.maxPushFolders
|
||||
idleRefreshMinutes = domain.idleRefreshMinutes
|
||||
useCompression = domain.useCompression
|
||||
isSendClientInfoEnabled = domain.isSendClientInfoEnabled
|
||||
isSubscribedFoldersOnly = domain.isSubscribedFoldersOnly
|
||||
maximumPolledMessageAge = domain.maximumPolledMessageAge
|
||||
maximumAutoDownloadMessageSize = domain.maximumAutoDownloadMessageSize
|
||||
messageFormat = domain.messageFormat
|
||||
isMessageFormatAuto = domain.isMessageFormatAuto
|
||||
isMessageReadReceipt = domain.isMessageReadReceipt
|
||||
quoteStyle = domain.quoteStyle
|
||||
quotePrefix = domain.quotePrefix
|
||||
isDefaultQuotedTextShown = domain.isDefaultQuotedTextShown
|
||||
isReplyAfterQuote = domain.isReplyAfterQuote
|
||||
isStripSignature = domain.isStripSignature
|
||||
isSyncRemoteDeletions = domain.isSyncRemoteDeletions
|
||||
openPgpProvider = domain.openPgpProvider
|
||||
openPgpKey = domain.openPgpKey
|
||||
autocryptPreferEncryptMutual = domain.autocryptPreferEncryptMutual
|
||||
isOpenPgpHideSignOnly = domain.isOpenPgpHideSignOnly
|
||||
isOpenPgpEncryptSubject = domain.isOpenPgpEncryptSubject
|
||||
isOpenPgpEncryptAllDrafts = domain.isOpenPgpEncryptAllDrafts
|
||||
isMarkMessageAsReadOnView = domain.isMarkMessageAsReadOnView
|
||||
isMarkMessageAsReadOnDelete = domain.isMarkMessageAsReadOnDelete
|
||||
isAlwaysShowCcBcc = domain.isAlwaysShowCcBcc
|
||||
isRemoteSearchFullText = domain.isRemoteSearchFullText
|
||||
remoteSearchNumResults = domain.remoteSearchNumResults
|
||||
isUploadSentMessages = domain.isUploadSentMessages
|
||||
lastSyncTime = domain.lastSyncTime
|
||||
lastFolderListRefreshTime = domain.lastFolderListRefreshTime
|
||||
isFinishedSetup = domain.isFinishedSetup
|
||||
messagesNotificationChannelVersion = domain.messagesNotificationChannelVersion
|
||||
isChangedVisibleLimits = domain.isChangedVisibleLimits
|
||||
lastSelectedFolderId = domain.lastSelectedFolderId
|
||||
notificationSettings = domain.notificationSettings
|
||||
senderName = domain.senderName
|
||||
signatureUse = domain.signatureUse
|
||||
signature = domain.signature
|
||||
shouldMigrateToOAuth = domain.shouldMigrateToOAuth
|
||||
folderPathDelimiter = domain.folderPathDelimiter
|
||||
}
|
||||
}
|
||||
|
||||
private fun fromProfileDto(dto: ProfileDto, account: LegacyAccount) {
|
||||
account.name = dto.name
|
||||
account.chipColor = dto.color
|
||||
account.avatar = dto.avatar
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.serializer
|
||||
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonReader.Token
|
||||
import com.squareup.moshi.JsonWriter
|
||||
|
||||
class ServerSettingsDtoSerializer {
|
||||
private val adapter = ServerSettingsAdapter()
|
||||
|
||||
fun serialize(serverSettings: ServerSettings): String {
|
||||
return adapter.toJson(serverSettings)
|
||||
}
|
||||
|
||||
fun deserialize(json: String): ServerSettings {
|
||||
return adapter.fromJson(json)!!
|
||||
}
|
||||
}
|
||||
|
||||
private const val KEY_TYPE = "type"
|
||||
private const val KEY_HOST = "host"
|
||||
private const val KEY_PORT = "port"
|
||||
private const val KEY_CONNECTION_SECURITY = "connectionSecurity"
|
||||
private const val KEY_AUTHENTICATION_TYPE = "authenticationType"
|
||||
private const val KEY_USERNAME = "username"
|
||||
private const val KEY_PASSWORD = "password"
|
||||
private const val KEY_CLIENT_CERTIFICATE_ALIAS = "clientCertificateAlias"
|
||||
|
||||
private val JSON_KEYS = JsonReader.Options.of(
|
||||
KEY_TYPE,
|
||||
KEY_HOST,
|
||||
KEY_PORT,
|
||||
KEY_CONNECTION_SECURITY,
|
||||
KEY_AUTHENTICATION_TYPE,
|
||||
KEY_USERNAME,
|
||||
KEY_PASSWORD,
|
||||
KEY_CLIENT_CERTIFICATE_ALIAS,
|
||||
)
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private class ServerSettingsAdapter : JsonAdapter<ServerSettings>() {
|
||||
override fun fromJson(reader: JsonReader): ServerSettings {
|
||||
reader.beginObject()
|
||||
|
||||
var type: String? = null
|
||||
var host: String? = null
|
||||
var port: Int? = null
|
||||
var connectionSecurity: ConnectionSecurity? = null
|
||||
var authenticationType: AuthType? = null
|
||||
var username: String? = null
|
||||
var password: String? = null
|
||||
var clientCertificateAlias: String? = null
|
||||
val extra = mutableMapOf<String, String?>()
|
||||
|
||||
while (reader.hasNext()) {
|
||||
when (reader.selectName(JSON_KEYS)) {
|
||||
0 -> type = reader.nextString()
|
||||
1 -> host = reader.nextString()
|
||||
2 -> port = reader.nextInt()
|
||||
3 -> connectionSecurity = ConnectionSecurity.valueOf(reader.nextString())
|
||||
4 -> authenticationType = AuthType.valueOf(reader.nextString())
|
||||
5 -> username = reader.nextString()
|
||||
6 -> password = reader.nextStringOrNull()
|
||||
7 -> clientCertificateAlias = reader.nextStringOrNull()
|
||||
else -> {
|
||||
val key = reader.nextName()
|
||||
val value = reader.nextStringOrNull()
|
||||
extra[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.endObject()
|
||||
|
||||
requireNotNull(type) { "'type' must not be missing" }
|
||||
requireNotNull(host) { "'host' must not be missing" }
|
||||
requireNotNull(port) { "'port' must not be missing" }
|
||||
requireNotNull(connectionSecurity) { "'connectionSecurity' must not be missing" }
|
||||
requireNotNull(authenticationType) { "'authenticationType' must not be missing" }
|
||||
requireNotNull(username) { "'username' must not be missing" }
|
||||
|
||||
return ServerSettings(
|
||||
type,
|
||||
host,
|
||||
port,
|
||||
connectionSecurity,
|
||||
authenticationType,
|
||||
username,
|
||||
password,
|
||||
clientCertificateAlias,
|
||||
extra,
|
||||
)
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, serverSettings: ServerSettings?) {
|
||||
requireNotNull(serverSettings)
|
||||
|
||||
writer.beginObject()
|
||||
writer.serializeNulls = true
|
||||
|
||||
writer.name(KEY_TYPE).value(serverSettings.type)
|
||||
writer.name(KEY_HOST).value(serverSettings.host)
|
||||
writer.name(KEY_PORT).value(serverSettings.port)
|
||||
writer.name(KEY_CONNECTION_SECURITY).value(serverSettings.connectionSecurity.name)
|
||||
writer.name(KEY_AUTHENTICATION_TYPE).value(serverSettings.authenticationType.name)
|
||||
writer.name(KEY_USERNAME).value(serverSettings.username)
|
||||
writer.name(KEY_PASSWORD).value(serverSettings.password)
|
||||
writer.name(KEY_CLIENT_CERTIFICATE_ALIAS).value(serverSettings.clientCertificateAlias)
|
||||
|
||||
for ((key, value) in serverSettings.extra) {
|
||||
writer.name(key).value(value)
|
||||
}
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
|
||||
private fun JsonReader.nextStringOrNull(): String? {
|
||||
return if (peek() == Token.NULL) nextNull() else nextString()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.isNotEqualTo
|
||||
import kotlin.test.Test
|
||||
import net.thunderbird.account.fake.FakeAccountData
|
||||
|
||||
class AccountKeyGeneratorTest {
|
||||
|
||||
@Test
|
||||
fun `create should combine account ID with key`() {
|
||||
// Arrange
|
||||
val accountId = FakeAccountData.ACCOUNT_ID
|
||||
val testSubject = AccountKeyGenerator(accountId)
|
||||
val key = "testKey"
|
||||
|
||||
// Act
|
||||
val result = testSubject.create(key)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo("${accountId.asRaw()}.$key")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create should fail with empty key`() {
|
||||
// Arrange
|
||||
val accountId = FakeAccountData.ACCOUNT_ID
|
||||
val testSubject = AccountKeyGenerator(accountId)
|
||||
val key = ""
|
||||
|
||||
// Act & Assert
|
||||
assertFailure {
|
||||
testSubject.create(key)
|
||||
}.isInstanceOf<IllegalArgumentException>()
|
||||
.hasMessage("Key must not be empty")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create should work with different account IDs`() {
|
||||
// Arrange
|
||||
val accountId1 = FakeAccountData.ACCOUNT_ID
|
||||
val accountId2 = FakeAccountData.ACCOUNT_ID_OTHER
|
||||
val testSubject1 = AccountKeyGenerator(accountId1)
|
||||
val testSubject2 = AccountKeyGenerator(accountId2)
|
||||
val key = "testKey"
|
||||
|
||||
// Act
|
||||
val result1 = testSubject1.create(key)
|
||||
val result2 = testSubject2.create(key)
|
||||
|
||||
// Assert
|
||||
assertThat(result1).isEqualTo("${accountId1.asRaw()}.$key")
|
||||
assertThat(result2).isEqualTo("${accountId2.asRaw()}.$key")
|
||||
assertThat(result1).isNotEqualTo(result2)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import kotlin.test.Test
|
||||
import org.koin.core.annotation.KoinExperimentalAPI
|
||||
import org.koin.test.verify.verify
|
||||
|
||||
class AccountStorageLegacyModuleKtTest {
|
||||
|
||||
@OptIn(KoinExperimentalAPI::class)
|
||||
@Test
|
||||
fun `should have a valid di module`() {
|
||||
featureAccountStorageLegacyModule.verify()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlin.test.Test
|
||||
import net.thunderbird.account.fake.FakeAccountAvatarData
|
||||
import net.thunderbird.account.fake.FakeAccountData
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
import net.thunderbird.feature.account.storage.legacy.fake.FakeStorage
|
||||
import net.thunderbird.feature.account.storage.legacy.fake.FakeStorageEditor
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
|
||||
class LegacyAvatarDtoStorageHandlerTest {
|
||||
private val testSubject = LegacyAvatarDtoStorageHandler()
|
||||
|
||||
@Test
|
||||
fun `load should populate avatar data from storage`() {
|
||||
// Arrange
|
||||
val account = createAccount(accountId)
|
||||
val storage = createStorage(accountId)
|
||||
|
||||
// Act
|
||||
testSubject.load(account, storage)
|
||||
|
||||
// Assert
|
||||
assertThat(account.avatar.avatarType).isEqualTo(AVATAR_TYPE)
|
||||
assertThat(account.avatar.avatarMonogram).isEqualTo(AVATAR_MONOGRAM)
|
||||
assertThat(account.avatar.avatarImageUri).isEqualTo(AVATAR_IMAGE_URI)
|
||||
assertThat(account.avatar.avatarIconName).isEqualTo(AVATAR_ICON_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save should store avatar data to storage`() {
|
||||
// Arrange
|
||||
val account = createAccount(accountId)
|
||||
val storage = FakeStorage()
|
||||
val editor = FakeStorageEditor()
|
||||
|
||||
// Act
|
||||
testSubject.save(account, storage, editor)
|
||||
|
||||
// Assert
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarType"]).isEqualTo(AVATAR_TYPE.name)
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarMonogram"]).isEqualTo(AVATAR_MONOGRAM)
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarImageUri"]).isEqualTo(AVATAR_IMAGE_URI)
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarIconName"]).isEqualTo(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete should remove avatar data from storage`() {
|
||||
// Arrange
|
||||
val account = createAccount(accountId)
|
||||
val storage = FakeStorage()
|
||||
val editor = FakeStorageEditor()
|
||||
|
||||
// Act
|
||||
testSubject.delete(account, storage, editor)
|
||||
|
||||
// Assert
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarType")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarMonogram")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarImageUri")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarIconName")
|
||||
}
|
||||
|
||||
// Arrange methods
|
||||
private fun createAccount(accountId: AccountId): LegacyAccount {
|
||||
return LegacyAccount(accountId.asRaw()).apply {
|
||||
name = "Test Account"
|
||||
chipColor = 0x0099CC // Default color
|
||||
avatar = AvatarDto(
|
||||
avatarType = AVATAR_TYPE,
|
||||
avatarMonogram = AVATAR_MONOGRAM,
|
||||
avatarImageUri = AVATAR_IMAGE_URI,
|
||||
avatarIconName = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStorage(accountId: AccountId): FakeStorage {
|
||||
return FakeStorage(
|
||||
mapOf(
|
||||
"${accountId.asRaw()}.avatarType" to AVATAR_TYPE.name,
|
||||
"${accountId.asRaw()}.avatarMonogram" to AVATAR_MONOGRAM,
|
||||
"${accountId.asRaw()}.avatarImageUri" to AVATAR_IMAGE_URI,
|
||||
"${accountId.asRaw()}.avatarIconName" to AVATAR_ICON_NAME,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val accountId = FakeAccountData.ACCOUNT_ID
|
||||
|
||||
val AVATAR_TYPE = AvatarTypeDto.MONOGRAM
|
||||
const val AVATAR_MONOGRAM = "TB"
|
||||
const val AVATAR_IMAGE_URI = FakeAccountAvatarData.AVATAR_IMAGE_URI
|
||||
const val AVATAR_ICON_NAME = "icon-name"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package net.thunderbird.feature.account.storage.legacy
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlin.test.Test
|
||||
import net.thunderbird.account.fake.FakeAccountAvatarData
|
||||
import net.thunderbird.account.fake.FakeAccountData
|
||||
import net.thunderbird.account.fake.FakeAccountProfileData
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
import net.thunderbird.feature.account.storage.legacy.fake.FakeStorage
|
||||
import net.thunderbird.feature.account.storage.legacy.fake.FakeStorageEditor
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
|
||||
class LegacyProfileDtoStorageHandlerTest {
|
||||
private val avatarDtoStorageHandler = LegacyAvatarDtoStorageHandler()
|
||||
private val testSubject = LegacyProfileDtoStorageHandler(avatarDtoStorageHandler)
|
||||
|
||||
@Test
|
||||
fun `load should populate profile data from storage`() {
|
||||
// Arrange
|
||||
val account = createAccount(accountId)
|
||||
val storage = createStorage(accountId)
|
||||
|
||||
// Act
|
||||
testSubject.load(account, storage)
|
||||
|
||||
// Assert
|
||||
assertThat(account.name).isEqualTo(NAME)
|
||||
assertThat(account.chipColor).isEqualTo(COLOR)
|
||||
assertThat(account.avatar.avatarType).isEqualTo(AvatarTypeDto.IMAGE)
|
||||
assertThat(account.avatar.avatarMonogram).isEqualTo(null)
|
||||
assertThat(account.avatar.avatarImageUri).isEqualTo(AVATAR_IMAGE_URI)
|
||||
assertThat(account.avatar.avatarIconName).isEqualTo(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save should store profile data to storage`() {
|
||||
// Arrange
|
||||
val account = createAccount(accountId)
|
||||
val storage = FakeStorage()
|
||||
val editor = FakeStorageEditor()
|
||||
|
||||
// Act
|
||||
testSubject.save(account, storage, editor)
|
||||
|
||||
// Assert
|
||||
assertThat(editor.values["${accountId.asRaw()}.description"]).isEqualTo(NAME)
|
||||
assertThat(editor.values["${accountId.asRaw()}.chipColor"]).isEqualTo(COLOR.toString())
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarType"]).isEqualTo("IMAGE")
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarMonogram"]).isEqualTo(null)
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarImageUri"]).isEqualTo(AVATAR_IMAGE_URI)
|
||||
assertThat(editor.values["${accountId.asRaw()}.avatarIconName"]).isEqualTo(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete should remove profile data from storage`() {
|
||||
// Arrange
|
||||
val account = createAccount(accountId)
|
||||
val storage = FakeStorage()
|
||||
val editor = FakeStorageEditor()
|
||||
|
||||
// Act
|
||||
testSubject.delete(account, storage, editor)
|
||||
|
||||
// Assert
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.description")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.chipColor")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarType")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarMonogram")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarImageUri")
|
||||
assertThat(editor.removedKeys).contains("${accountId.asRaw()}.avatarIconName")
|
||||
}
|
||||
|
||||
// Arrange methods
|
||||
private fun createAccount(accountId: AccountId): LegacyAccount {
|
||||
return LegacyAccount(accountId.asRaw()).apply {
|
||||
name = NAME
|
||||
chipColor = COLOR
|
||||
avatar = AvatarDto(
|
||||
avatarType = AvatarTypeDto.IMAGE,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = AVATAR_IMAGE_URI,
|
||||
avatarIconName = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStorage(accountId: AccountId): FakeStorage {
|
||||
return FakeStorage(
|
||||
mapOf(
|
||||
"${accountId.asRaw()}.description" to NAME,
|
||||
"${accountId.asRaw()}.chipColor" to COLOR.toString(),
|
||||
"${accountId.asRaw()}.avatarType" to AVATAR_TYPE.name,
|
||||
"${accountId.asRaw()}.avatarImageUri" to AVATAR_IMAGE_URI,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val accountId = FakeAccountData.ACCOUNT_ID
|
||||
|
||||
const val NAME = FakeAccountProfileData.PROFILE_NAME
|
||||
const val COLOR = FakeAccountProfileData.PROFILE_COLOR
|
||||
|
||||
val AVATAR_TYPE = AvatarTypeDto.IMAGE
|
||||
const val AVATAR_IMAGE_URI = FakeAccountAvatarData.AVATAR_IMAGE_URI
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.fake
|
||||
|
||||
import net.thunderbird.core.preference.storage.Storage
|
||||
|
||||
class FakeStorage(private val values: Map<String, String> = emptyMap()) : Storage {
|
||||
override fun isEmpty(): Boolean = values.isEmpty()
|
||||
|
||||
override fun contains(key: String): Boolean = values.containsKey(key)
|
||||
|
||||
override fun getAll(): Map<String, String> = values
|
||||
|
||||
override fun getBoolean(key: String, defValue: Boolean): Boolean =
|
||||
values[key]?.toBoolean() ?: defValue
|
||||
|
||||
override fun getInt(key: String, defValue: Int): Int =
|
||||
values[key]?.toIntOrNull() ?: defValue
|
||||
|
||||
override fun getLong(key: String, defValue: Long): Long =
|
||||
values[key]?.toLongOrNull() ?: defValue
|
||||
|
||||
override fun getString(key: String): String =
|
||||
values[key] ?: throw NoSuchElementException("No value for key $key")
|
||||
|
||||
override fun getStringOrDefault(key: String, defValue: String): String =
|
||||
values[key] ?: defValue
|
||||
|
||||
override fun getStringOrNull(key: String): String? = values[key]
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.fake
|
||||
|
||||
import net.thunderbird.core.preference.storage.StorageEditor
|
||||
|
||||
class FakeStorageEditor : StorageEditor {
|
||||
val values = mutableMapOf<String, String?>()
|
||||
|
||||
val removedKeys = mutableListOf<String>()
|
||||
|
||||
override fun putBoolean(key: String, value: Boolean): StorageEditor {
|
||||
values[key] = value.toString()
|
||||
return this
|
||||
}
|
||||
|
||||
override fun putInt(key: String, value: Int): StorageEditor {
|
||||
values[key] = value.toString()
|
||||
return this
|
||||
}
|
||||
|
||||
override fun putLong(key: String, value: Long): StorageEditor {
|
||||
values[key] = value.toString()
|
||||
return this
|
||||
}
|
||||
|
||||
override fun putString(key: String, value: String?): StorageEditor {
|
||||
values[key] = value
|
||||
return this
|
||||
}
|
||||
|
||||
override fun remove(key: String): StorageEditor {
|
||||
values.remove(key)
|
||||
removedKeys.add(key)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun commit(): Boolean = true
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isNull
|
||||
import kotlin.test.Test
|
||||
import net.thunderbird.feature.account.profile.AccountAvatar
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
|
||||
class DefaultAccountAvatarDataMapperTest {
|
||||
|
||||
private val testSubject = DefaultAccountAvatarDataMapper()
|
||||
|
||||
@Test
|
||||
fun `toDomain should map valid AvatarDto to correct AccountAvatar type`() {
|
||||
require(testCases.isNotEmpty()) { "Test cases should not be empty" }
|
||||
|
||||
testCases.forEach { case ->
|
||||
// Arrange
|
||||
val dto = case.dto
|
||||
val expected = case.domain
|
||||
|
||||
// Act
|
||||
val result = testSubject.toDomain(dto)
|
||||
|
||||
// Assert
|
||||
when (result) {
|
||||
is AccountAvatar.Monogram -> assertDomainMonogram(result, expected)
|
||||
is AccountAvatar.Image -> assertDomainImage(result, expected)
|
||||
is AccountAvatar.Icon -> assertDomainIcon(result, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toDomain should return default monogram for invalid AvatarDto`() {
|
||||
val avatarTypeDtos = AvatarTypeDto.entries
|
||||
|
||||
avatarTypeDtos.forEach { type ->
|
||||
// Arrange
|
||||
val dto = AvatarDto(
|
||||
avatarType = type,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = null,
|
||||
avatarIconName = null,
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = testSubject.toDomain(dto)
|
||||
|
||||
// Assert
|
||||
assertDomainMonogram(result, AccountAvatar.Monogram("XX"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toDto should map valid AccountAvatar to correct AvatarDto type`() {
|
||||
require(testCases.isNotEmpty()) { "Test cases should not be empty" }
|
||||
|
||||
testCases.forEach { case ->
|
||||
// Arrange
|
||||
val domain = case.domain
|
||||
val expected = case.dto
|
||||
|
||||
// Act
|
||||
val result = testSubject.toDto(domain)
|
||||
|
||||
// Assert
|
||||
when (result.avatarType) {
|
||||
AvatarTypeDto.MONOGRAM -> assertDtoMonogram(result, expected)
|
||||
AvatarTypeDto.IMAGE -> assertDtoImage(result, expected)
|
||||
AvatarTypeDto.ICON -> assertDtoIcon(result, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertDomainMonogram(actual: AccountAvatar, expected: AccountAvatar) {
|
||||
require(expected is AccountAvatar.Monogram) { "Expected AccountAvatar to be of type Monogram" }
|
||||
assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
private fun assertDomainImage(actual: AccountAvatar, expected: AccountAvatar) {
|
||||
require(expected is AccountAvatar.Image) { "Expected AccountAvatar to be of type Image" }
|
||||
assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
private fun assertDomainIcon(actual: AccountAvatar, expected: AccountAvatar) {
|
||||
require(expected is AccountAvatar.Icon) { "Expected AccountAvatar to be of type Icon" }
|
||||
assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
private fun assertDtoMonogram(actual: AvatarDto, expected: AvatarDto) {
|
||||
assertThat(actual.avatarType).isEqualTo(AvatarTypeDto.MONOGRAM)
|
||||
assertThat(actual.avatarMonogram).isEqualTo(expected.avatarMonogram)
|
||||
assertThat(actual.avatarImageUri).isNull()
|
||||
assertThat(actual.avatarIconName).isNull()
|
||||
}
|
||||
|
||||
private fun assertDtoImage(actual: AvatarDto, expected: AvatarDto) {
|
||||
assertThat(actual.avatarType).isEqualTo(AvatarTypeDto.IMAGE)
|
||||
assertThat(actual.avatarMonogram).isNull()
|
||||
assertThat(actual.avatarImageUri).isEqualTo(expected.avatarImageUri)
|
||||
assertThat(actual.avatarIconName).isNull()
|
||||
}
|
||||
|
||||
private fun assertDtoIcon(actual: AvatarDto, expected: AvatarDto) {
|
||||
assertThat(actual.avatarType).isEqualTo(AvatarTypeDto.ICON)
|
||||
assertThat(actual.avatarMonogram).isNull()
|
||||
assertThat(actual.avatarImageUri).isNull()
|
||||
assertThat(actual.avatarIconName).isEqualTo(expected.avatarIconName)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
data class TestCase(
|
||||
val dto: AvatarDto,
|
||||
val domain: AccountAvatar,
|
||||
)
|
||||
|
||||
val testCases = listOf(
|
||||
TestCase(
|
||||
AvatarDto(AvatarTypeDto.MONOGRAM, "AB", null, null),
|
||||
AccountAvatar.Monogram("AB"),
|
||||
),
|
||||
TestCase(
|
||||
AvatarDto(AvatarTypeDto.IMAGE, null, "uri://img", null),
|
||||
AccountAvatar.Image("uri://img"),
|
||||
),
|
||||
TestCase(
|
||||
AvatarDto(AvatarTypeDto.ICON, null, null, "icon_name"),
|
||||
AccountAvatar.Icon("icon_name"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import net.thunderbird.account.fake.FakeAccountAvatarData.AVATAR_IMAGE_URI
|
||||
import net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID
|
||||
import net.thunderbird.account.fake.FakeAccountProfileData.PROFILE_COLOR
|
||||
import net.thunderbird.account.fake.FakeAccountProfileData.PROFILE_NAME
|
||||
import net.thunderbird.account.fake.FakeAccountProfileData.createAccountProfile
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
import net.thunderbird.feature.account.storage.profile.ProfileDto
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultAccountProfileDataMapperTest {
|
||||
|
||||
@Test
|
||||
fun `toDomain should convert ProfileDto to AccountProfile`() {
|
||||
// Arrange
|
||||
val dto = createProfileDto()
|
||||
val expected = createAccountProfile()
|
||||
|
||||
val testSubject = DefaultAccountProfileDataMapper(
|
||||
avatarMapper = FakeAccountAvatarDataMapper(
|
||||
dto = dto.avatar,
|
||||
domain = expected.avatar,
|
||||
),
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = testSubject.toDomain(dto)
|
||||
|
||||
// Assert
|
||||
assertThat(result.id).isEqualTo(expected.id)
|
||||
assertThat(result.name).isEqualTo(expected.name)
|
||||
assertThat(result.color).isEqualTo(expected.color)
|
||||
assertThat(result.avatar).isEqualTo(expected.avatar)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toDto should convert AccountProfile to ProfileDto`() {
|
||||
// Arrange
|
||||
val domain = createAccountProfile()
|
||||
val expected = createProfileDto()
|
||||
|
||||
val testSubject = DefaultAccountProfileDataMapper(
|
||||
avatarMapper = FakeAccountAvatarDataMapper(
|
||||
dto = expected.avatar,
|
||||
domain = domain.avatar,
|
||||
),
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = testSubject.toDto(domain)
|
||||
|
||||
// Assert
|
||||
assertThat(result.id).isEqualTo(expected.id)
|
||||
assertThat(result.name).isEqualTo(expected.name)
|
||||
assertThat(result.color).isEqualTo(expected.color)
|
||||
assertThat(result.avatar).isEqualTo(expected.avatar)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
fun createProfileDto(
|
||||
id: AccountId = ACCOUNT_ID,
|
||||
name: String = PROFILE_NAME,
|
||||
color: Int = PROFILE_COLOR,
|
||||
avatar: AvatarDto = AvatarDto(
|
||||
avatarType = AvatarTypeDto.IMAGE,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = AVATAR_IMAGE_URI,
|
||||
avatarIconName = null,
|
||||
),
|
||||
): ProfileDto {
|
||||
return ProfileDto(
|
||||
id = id,
|
||||
name = name,
|
||||
color = color,
|
||||
avatar = avatar,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import kotlin.test.Test
|
||||
import net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID_RAW
|
||||
import net.thunderbird.core.android.account.DeletePolicy
|
||||
import net.thunderbird.core.android.account.Expunge
|
||||
import net.thunderbird.core.android.account.FolderMode
|
||||
import net.thunderbird.core.android.account.Identity
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.android.account.LegacyAccountWrapper
|
||||
import net.thunderbird.core.android.account.MessageFormat
|
||||
import net.thunderbird.core.android.account.QuoteStyle
|
||||
import net.thunderbird.core.android.account.ShowPictures
|
||||
import net.thunderbird.core.android.account.SortType
|
||||
import net.thunderbird.feature.account.AccountIdFactory
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarTypeDto
|
||||
import net.thunderbird.feature.account.storage.profile.ProfileDto
|
||||
import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection
|
||||
import net.thunderbird.feature.notification.NotificationSettings
|
||||
|
||||
class DefaultLegacyAccountWrapperDataMapperTest {
|
||||
|
||||
@Test
|
||||
fun `toDomain should return wrapper`() {
|
||||
// arrange
|
||||
val account = createAccount()
|
||||
val expected = createAccountWrapper()
|
||||
val testSubject = DefaultLegacyAccountWrapperDataMapper()
|
||||
|
||||
// act
|
||||
val result = testSubject.toDomain(account)
|
||||
|
||||
// assert
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Test
|
||||
fun `toDto should return account`() {
|
||||
// arrange
|
||||
val wrapper = createAccountWrapper()
|
||||
val testSubject = DefaultLegacyAccountWrapperDataMapper()
|
||||
|
||||
// act
|
||||
val result = testSubject.toDto(wrapper)
|
||||
|
||||
// assert
|
||||
assertThat(result.id).isEqualTo(AccountIdFactory.of(ACCOUNT_ID_RAW))
|
||||
assertThat(result.uuid).isEqualTo(ACCOUNT_ID_RAW)
|
||||
assertThat(result.isSensitiveDebugLoggingEnabled).isEqualTo(defaultIsSensitiveDebugLoggingEnabled)
|
||||
assertThat(result.identities).isEqualTo(defaultIdentities)
|
||||
assertThat(result.name).isEqualTo("displayName")
|
||||
assertThat(result.email).isEqualTo("demo@example.com")
|
||||
assertThat(result.deletePolicy).isEqualTo(DeletePolicy.SEVEN_DAYS)
|
||||
assertThat(result.incomingServerSettings).isEqualTo(defaultIncomingServerSettings)
|
||||
assertThat(result.outgoingServerSettings).isEqualTo(defaultOutgoingServerSettings)
|
||||
assertThat(result.oAuthState).isEqualTo("oAuthState")
|
||||
assertThat(result.alwaysBcc).isEqualTo("alwaysBcc")
|
||||
assertThat(result.automaticCheckIntervalMinutes).isEqualTo(60)
|
||||
assertThat(result.displayCount).isEqualTo(10)
|
||||
assertThat(result.chipColor).isEqualTo(0xFFFF0000.toInt())
|
||||
assertThat(result.isNotifyNewMail).isEqualTo(true)
|
||||
assertThat(result.folderNotifyNewMailMode).isEqualTo(FolderMode.FIRST_AND_SECOND_CLASS)
|
||||
assertThat(result.isNotifySelfNewMail).isEqualTo(true)
|
||||
assertThat(result.isNotifyContactsMailOnly).isEqualTo(true)
|
||||
assertThat(result.isIgnoreChatMessages).isEqualTo(true)
|
||||
assertThat(result.legacyInboxFolder).isEqualTo("legacyInboxFolder")
|
||||
assertThat(result.importedDraftsFolder).isEqualTo("importedDraftsFolder")
|
||||
assertThat(result.importedSentFolder).isEqualTo("importedSentFolder")
|
||||
assertThat(result.importedTrashFolder).isEqualTo("importedTrashFolder")
|
||||
assertThat(result.importedArchiveFolder).isEqualTo("importedArchiveFolder")
|
||||
assertThat(result.importedSpamFolder).isEqualTo("importedSpamFolder")
|
||||
assertThat(result.inboxFolderId).isEqualTo(1)
|
||||
assertThat(result.draftsFolderId).isEqualTo(3)
|
||||
assertThat(result.sentFolderId).isEqualTo(4)
|
||||
assertThat(result.trashFolderId).isEqualTo(5)
|
||||
assertThat(result.archiveFolderId).isEqualTo(6)
|
||||
assertThat(result.spamFolderId).isEqualTo(7)
|
||||
assertThat(result.draftsFolderSelection).isEqualTo(SpecialFolderSelection.MANUAL)
|
||||
assertThat(result.sentFolderSelection).isEqualTo(SpecialFolderSelection.MANUAL)
|
||||
assertThat(result.trashFolderSelection).isEqualTo(SpecialFolderSelection.MANUAL)
|
||||
assertThat(result.archiveFolderSelection).isEqualTo(SpecialFolderSelection.MANUAL)
|
||||
assertThat(result.spamFolderSelection).isEqualTo(SpecialFolderSelection.MANUAL)
|
||||
assertThat(result.importedAutoExpandFolder).isEqualTo("importedAutoExpandFolder")
|
||||
assertThat(result.autoExpandFolderId).isEqualTo(8)
|
||||
assertThat(result.folderDisplayMode).isEqualTo(FolderMode.FIRST_AND_SECOND_CLASS)
|
||||
assertThat(result.folderSyncMode).isEqualTo(FolderMode.FIRST_AND_SECOND_CLASS)
|
||||
assertThat(result.folderPushMode).isEqualTo(FolderMode.FIRST_AND_SECOND_CLASS)
|
||||
assertThat(result.accountNumber).isEqualTo(11)
|
||||
assertThat(result.isNotifySync).isEqualTo(true)
|
||||
assertThat(result.sortType).isEqualTo(SortType.SORT_SUBJECT)
|
||||
assertThat(result.sortAscending).isEqualTo(
|
||||
mutableMapOf(
|
||||
SortType.SORT_SUBJECT to false,
|
||||
),
|
||||
)
|
||||
assertThat(result.showPictures).isEqualTo(ShowPictures.ALWAYS)
|
||||
assertThat(result.isSignatureBeforeQuotedText).isEqualTo(true)
|
||||
assertThat(result.expungePolicy).isEqualTo(Expunge.EXPUNGE_MANUALLY)
|
||||
assertThat(result.maxPushFolders).isEqualTo(12)
|
||||
assertThat(result.idleRefreshMinutes).isEqualTo(13)
|
||||
assertThat(result.useCompression).isEqualTo(false)
|
||||
assertThat(result.isSendClientInfoEnabled).isEqualTo(false)
|
||||
assertThat(result.isSubscribedFoldersOnly).isEqualTo(false)
|
||||
assertThat(result.maximumPolledMessageAge).isEqualTo(14)
|
||||
assertThat(result.maximumAutoDownloadMessageSize).isEqualTo(15)
|
||||
assertThat(result.messageFormat).isEqualTo(MessageFormat.TEXT)
|
||||
assertThat(result.isMessageFormatAuto).isEqualTo(true)
|
||||
assertThat(result.isMessageReadReceipt).isEqualTo(true)
|
||||
assertThat(result.quoteStyle).isEqualTo(QuoteStyle.HEADER)
|
||||
assertThat(result.quotePrefix).isEqualTo("quotePrefix")
|
||||
assertThat(result.isDefaultQuotedTextShown).isEqualTo(true)
|
||||
assertThat(result.isReplyAfterQuote).isEqualTo(true)
|
||||
assertThat(result.isStripSignature).isEqualTo(true)
|
||||
assertThat(result.isSyncRemoteDeletions).isEqualTo(true)
|
||||
assertThat(result.openPgpProvider).isEqualTo("openPgpProvider")
|
||||
assertThat(result.openPgpKey).isEqualTo(16)
|
||||
assertThat(result.autocryptPreferEncryptMutual).isEqualTo(true)
|
||||
assertThat(result.isOpenPgpHideSignOnly).isEqualTo(true)
|
||||
assertThat(result.isOpenPgpEncryptSubject).isEqualTo(true)
|
||||
assertThat(result.isOpenPgpEncryptAllDrafts).isEqualTo(true)
|
||||
assertThat(result.isMarkMessageAsReadOnView).isEqualTo(true)
|
||||
assertThat(result.isMarkMessageAsReadOnDelete).isEqualTo(true)
|
||||
assertThat(result.isAlwaysShowCcBcc).isEqualTo(true)
|
||||
assertThat(result.isRemoteSearchFullText).isEqualTo(false)
|
||||
assertThat(result.remoteSearchNumResults).isEqualTo(17)
|
||||
assertThat(result.isUploadSentMessages).isEqualTo(true)
|
||||
assertThat(result.lastSyncTime).isEqualTo(18)
|
||||
assertThat(result.lastFolderListRefreshTime).isEqualTo(19)
|
||||
assertThat(result.isFinishedSetup).isEqualTo(true)
|
||||
assertThat(result.messagesNotificationChannelVersion).isEqualTo(20)
|
||||
assertThat(result.isChangedVisibleLimits).isEqualTo(true)
|
||||
assertThat(result.lastSelectedFolderId).isEqualTo(21)
|
||||
assertThat(result.notificationSettings).isEqualTo(defaultNotificationSettings)
|
||||
assertThat(result.senderName).isEqualTo(defaultIdentities[0].name)
|
||||
assertThat(result.signatureUse).isEqualTo(defaultIdentities[0].signatureUse)
|
||||
assertThat(result.signature).isEqualTo(defaultIdentities[0].signature)
|
||||
assertThat(result.shouldMigrateToOAuth).isEqualTo(true)
|
||||
assertThat(result.folderPathDelimiter).isEqualTo(".")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val defaultIsSensitiveDebugLoggingEnabled = { true }
|
||||
|
||||
val defaultIncomingServerSettings = ServerSettings(
|
||||
type = "imap",
|
||||
host = "imap.example.com",
|
||||
port = 993,
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "test",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
)
|
||||
|
||||
val defaultOutgoingServerSettings = ServerSettings(
|
||||
type = "smtp",
|
||||
host = "smtp.example.com",
|
||||
port = 465,
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "test",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
)
|
||||
|
||||
val defaultIdentities = mutableListOf(
|
||||
Identity(
|
||||
email = "demo@example.com",
|
||||
name = "identityName",
|
||||
signatureUse = true,
|
||||
signature = "signature",
|
||||
description = "Demo User",
|
||||
),
|
||||
)
|
||||
|
||||
val defaultNotificationSettings = NotificationSettings()
|
||||
|
||||
@Suppress("LongMethod")
|
||||
fun createAccount(): LegacyAccount {
|
||||
return LegacyAccount(
|
||||
uuid = ACCOUNT_ID_RAW,
|
||||
isSensitiveDebugLoggingEnabled = defaultIsSensitiveDebugLoggingEnabled,
|
||||
).apply {
|
||||
identities = defaultIdentities
|
||||
|
||||
// [BaseAccount]
|
||||
name = "displayName"
|
||||
email = "demo@example.com"
|
||||
|
||||
// [AccountProfile]
|
||||
chipColor = 0xFFFF0000.toInt()
|
||||
avatar = AvatarDto(
|
||||
avatarType = AvatarTypeDto.ICON,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = null,
|
||||
avatarIconName = "star",
|
||||
)
|
||||
|
||||
// Uncategorized
|
||||
deletePolicy = DeletePolicy.SEVEN_DAYS
|
||||
incomingServerSettings = defaultIncomingServerSettings
|
||||
outgoingServerSettings = defaultOutgoingServerSettings
|
||||
oAuthState = "oAuthState"
|
||||
alwaysBcc = "alwaysBcc"
|
||||
automaticCheckIntervalMinutes = 60
|
||||
displayCount = 10
|
||||
isNotifyNewMail = true
|
||||
folderNotifyNewMailMode = FolderMode.FIRST_AND_SECOND_CLASS
|
||||
isNotifySelfNewMail = true
|
||||
isNotifyContactsMailOnly = true
|
||||
isIgnoreChatMessages = true
|
||||
legacyInboxFolder = "legacyInboxFolder"
|
||||
importedDraftsFolder = "importedDraftsFolder"
|
||||
importedSentFolder = "importedSentFolder"
|
||||
importedTrashFolder = "importedTrashFolder"
|
||||
importedArchiveFolder = "importedArchiveFolder"
|
||||
importedSpamFolder = "importedSpamFolder"
|
||||
inboxFolderId = 1
|
||||
draftsFolderId = 3
|
||||
sentFolderId = 4
|
||||
trashFolderId = 5
|
||||
archiveFolderId = 6
|
||||
spamFolderId = 7
|
||||
draftsFolderSelection = SpecialFolderSelection.MANUAL
|
||||
sentFolderSelection = SpecialFolderSelection.MANUAL
|
||||
trashFolderSelection = SpecialFolderSelection.MANUAL
|
||||
archiveFolderSelection = SpecialFolderSelection.MANUAL
|
||||
spamFolderSelection = SpecialFolderSelection.MANUAL
|
||||
importedAutoExpandFolder = "importedAutoExpandFolder"
|
||||
autoExpandFolderId = 8
|
||||
folderDisplayMode = FolderMode.FIRST_AND_SECOND_CLASS
|
||||
folderSyncMode = FolderMode.FIRST_AND_SECOND_CLASS
|
||||
folderPushMode = FolderMode.FIRST_AND_SECOND_CLASS
|
||||
accountNumber = 11
|
||||
isNotifySync = true
|
||||
sortType = SortType.SORT_SUBJECT
|
||||
sortAscending = mutableMapOf(
|
||||
SortType.SORT_SUBJECT to false,
|
||||
)
|
||||
showPictures = ShowPictures.ALWAYS
|
||||
isSignatureBeforeQuotedText = true
|
||||
expungePolicy = Expunge.EXPUNGE_MANUALLY
|
||||
maxPushFolders = 12
|
||||
idleRefreshMinutes = 13
|
||||
useCompression = false
|
||||
isSendClientInfoEnabled = false
|
||||
isSubscribedFoldersOnly = false
|
||||
maximumPolledMessageAge = 14
|
||||
maximumAutoDownloadMessageSize = 15
|
||||
messageFormat = MessageFormat.TEXT
|
||||
isMessageFormatAuto = true
|
||||
isMessageReadReceipt = true
|
||||
quoteStyle = QuoteStyle.HEADER
|
||||
quotePrefix = "quotePrefix"
|
||||
isDefaultQuotedTextShown = true
|
||||
isReplyAfterQuote = true
|
||||
isStripSignature = true
|
||||
isSyncRemoteDeletions = true
|
||||
openPgpProvider = "openPgpProvider"
|
||||
openPgpKey = 16
|
||||
autocryptPreferEncryptMutual = true
|
||||
isOpenPgpHideSignOnly = true
|
||||
isOpenPgpEncryptSubject = true
|
||||
isOpenPgpEncryptAllDrafts = true
|
||||
isMarkMessageAsReadOnView = true
|
||||
isMarkMessageAsReadOnDelete = true
|
||||
isAlwaysShowCcBcc = true
|
||||
isRemoteSearchFullText = false
|
||||
remoteSearchNumResults = 17
|
||||
isUploadSentMessages = true
|
||||
lastSyncTime = 18
|
||||
lastFolderListRefreshTime = 19
|
||||
isFinishedSetup = true
|
||||
messagesNotificationChannelVersion = 20
|
||||
isChangedVisibleLimits = true
|
||||
lastSelectedFolderId = 21
|
||||
notificationSettings = defaultNotificationSettings
|
||||
senderName = defaultIdentities[0].name
|
||||
signatureUse = defaultIdentities[0].signatureUse
|
||||
signature = defaultIdentities[0].signature
|
||||
shouldMigrateToOAuth = true
|
||||
folderPathDelimiter = "."
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
fun createAccountWrapper(): LegacyAccountWrapper {
|
||||
val id = AccountIdFactory.of(ACCOUNT_ID_RAW)
|
||||
|
||||
return LegacyAccountWrapper(
|
||||
isSensitiveDebugLoggingEnabled = defaultIsSensitiveDebugLoggingEnabled,
|
||||
|
||||
// [Account]
|
||||
id = id,
|
||||
|
||||
// [BaseAccount]
|
||||
name = "displayName",
|
||||
email = "demo@example.com",
|
||||
|
||||
// [AccountProfile]
|
||||
profile = ProfileDto(
|
||||
id = id,
|
||||
name = "displayName",
|
||||
color = 0xFFFF0000.toInt(),
|
||||
avatar = AvatarDto(
|
||||
avatarType = AvatarTypeDto.ICON,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = null,
|
||||
avatarIconName = "star",
|
||||
),
|
||||
),
|
||||
|
||||
// Uncategorized
|
||||
deletePolicy = DeletePolicy.SEVEN_DAYS,
|
||||
incomingServerSettings = defaultIncomingServerSettings,
|
||||
outgoingServerSettings = defaultOutgoingServerSettings,
|
||||
oAuthState = "oAuthState",
|
||||
alwaysBcc = "alwaysBcc",
|
||||
automaticCheckIntervalMinutes = 60,
|
||||
displayCount = 10,
|
||||
isNotifyNewMail = true,
|
||||
folderNotifyNewMailMode = FolderMode.FIRST_AND_SECOND_CLASS,
|
||||
isNotifySelfNewMail = true,
|
||||
isNotifyContactsMailOnly = true,
|
||||
isIgnoreChatMessages = true,
|
||||
legacyInboxFolder = "legacyInboxFolder",
|
||||
importedDraftsFolder = "importedDraftsFolder",
|
||||
importedSentFolder = "importedSentFolder",
|
||||
importedTrashFolder = "importedTrashFolder",
|
||||
importedArchiveFolder = "importedArchiveFolder",
|
||||
importedSpamFolder = "importedSpamFolder",
|
||||
inboxFolderId = 1,
|
||||
draftsFolderId = 3,
|
||||
sentFolderId = 4,
|
||||
trashFolderId = 5,
|
||||
archiveFolderId = 6,
|
||||
spamFolderId = 7,
|
||||
draftsFolderSelection = SpecialFolderSelection.MANUAL,
|
||||
sentFolderSelection = SpecialFolderSelection.MANUAL,
|
||||
trashFolderSelection = SpecialFolderSelection.MANUAL,
|
||||
archiveFolderSelection = SpecialFolderSelection.MANUAL,
|
||||
spamFolderSelection = SpecialFolderSelection.MANUAL,
|
||||
importedAutoExpandFolder = "importedAutoExpandFolder",
|
||||
autoExpandFolderId = 8,
|
||||
folderDisplayMode = FolderMode.FIRST_AND_SECOND_CLASS,
|
||||
folderSyncMode = FolderMode.FIRST_AND_SECOND_CLASS,
|
||||
folderPushMode = FolderMode.FIRST_AND_SECOND_CLASS,
|
||||
accountNumber = 11,
|
||||
isNotifySync = true,
|
||||
sortType = SortType.SORT_SUBJECT,
|
||||
sortAscending = mutableMapOf(
|
||||
SortType.SORT_SUBJECT to false,
|
||||
),
|
||||
showPictures = ShowPictures.ALWAYS,
|
||||
isSignatureBeforeQuotedText = true,
|
||||
expungePolicy = Expunge.EXPUNGE_MANUALLY,
|
||||
maxPushFolders = 12,
|
||||
idleRefreshMinutes = 13,
|
||||
useCompression = false,
|
||||
isSendClientInfoEnabled = false,
|
||||
isSubscribedFoldersOnly = false,
|
||||
maximumPolledMessageAge = 14,
|
||||
maximumAutoDownloadMessageSize = 15,
|
||||
messageFormat = MessageFormat.TEXT,
|
||||
isMessageFormatAuto = true,
|
||||
isMessageReadReceipt = true,
|
||||
quoteStyle = QuoteStyle.HEADER,
|
||||
quotePrefix = "quotePrefix",
|
||||
isDefaultQuotedTextShown = true,
|
||||
isReplyAfterQuote = true,
|
||||
isStripSignature = true,
|
||||
isSyncRemoteDeletions = true,
|
||||
openPgpProvider = "openPgpProvider",
|
||||
openPgpKey = 16,
|
||||
autocryptPreferEncryptMutual = true,
|
||||
isOpenPgpHideSignOnly = true,
|
||||
isOpenPgpEncryptSubject = true,
|
||||
isOpenPgpEncryptAllDrafts = true,
|
||||
isMarkMessageAsReadOnView = true,
|
||||
isMarkMessageAsReadOnDelete = true,
|
||||
isAlwaysShowCcBcc = true,
|
||||
isRemoteSearchFullText = false,
|
||||
remoteSearchNumResults = 17,
|
||||
isUploadSentMessages = true,
|
||||
lastSyncTime = 18,
|
||||
lastFolderListRefreshTime = 19,
|
||||
isFinishedSetup = true,
|
||||
messagesNotificationChannelVersion = 20,
|
||||
isChangedVisibleLimits = true,
|
||||
lastSelectedFolderId = 21,
|
||||
identities = defaultIdentities,
|
||||
notificationSettings = defaultNotificationSettings,
|
||||
senderName = defaultIdentities[0].name,
|
||||
signatureUse = defaultIdentities[0].signatureUse,
|
||||
signature = defaultIdentities[0].signature,
|
||||
shouldMigrateToOAuth = true,
|
||||
folderPathDelimiter = ".",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.mapper
|
||||
|
||||
import net.thunderbird.feature.account.profile.AccountAvatar
|
||||
import net.thunderbird.feature.account.storage.mapper.AccountAvatarDataMapper
|
||||
import net.thunderbird.feature.account.storage.profile.AvatarDto
|
||||
|
||||
class FakeAccountAvatarDataMapper(
|
||||
private val dto: AvatarDto,
|
||||
private val domain: AccountAvatar,
|
||||
) : AccountAvatarDataMapper {
|
||||
override fun toDomain(dto: AvatarDto): AccountAvatar = domain
|
||||
|
||||
override fun toDto(domain: AccountAvatar): AvatarDto = dto
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package net.thunderbird.feature.account.storage.legacy.serializer
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import com.fsck.k9.mail.store.imap.ImapStoreSettings
|
||||
import kotlin.test.Test
|
||||
|
||||
class ServerSettingsSerializerTest {
|
||||
private val serverSettingsDtoSerializer = ServerSettingsDtoSerializer()
|
||||
|
||||
@Test
|
||||
fun `serialize and deserialize IMAP server settings`() {
|
||||
val serverSettings = ServerSettings(
|
||||
type = "imap",
|
||||
host = "imap.domain.example",
|
||||
port = 143,
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "user",
|
||||
password = null,
|
||||
clientCertificateAlias = "alias",
|
||||
extra = ImapStoreSettings.createExtra(autoDetectNamespace = true, pathPrefix = null),
|
||||
)
|
||||
|
||||
val json = serverSettingsDtoSerializer.serialize(serverSettings)
|
||||
val deserializedServerSettings = serverSettingsDtoSerializer.deserialize(json)
|
||||
|
||||
assertThat(deserializedServerSettings).isEqualTo(serverSettings)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serialize and deserialize POP3 server settings`() {
|
||||
val serverSettings = ServerSettings(
|
||||
type = "pop3",
|
||||
host = "pop3.domain.example",
|
||||
port = 995,
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "user",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
)
|
||||
|
||||
val json = serverSettingsDtoSerializer.serialize(serverSettings)
|
||||
val deserializedServerSettings = serverSettingsDtoSerializer.deserialize(json)
|
||||
|
||||
assertThat(deserializedServerSettings).isEqualTo(serverSettings)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deserialize JSON with missing type`() {
|
||||
val json = """
|
||||
{
|
||||
"host": "imap.domain.example",
|
||||
"port": 993,
|
||||
"connectionSecurity": "SSL_TLS_REQUIRED",
|
||||
"authenticationType": "PLAIN",
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
"clientCertificateAlias": null
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
assertFailure {
|
||||
serverSettingsDtoSerializer.deserialize(json)
|
||||
}.isInstanceOf<IllegalArgumentException>()
|
||||
.hasMessage("'type' must not be missing")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue