Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
26
core/android/account/build.gradle.kts
Normal file
26
core/android/account/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.android)
|
||||
alias(libs.plugins.kotlin.parcelize)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "net.thunderbird.core.android.account"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.feature.account.api)
|
||||
api(projects.feature.account.storage.api)
|
||||
|
||||
implementation(projects.feature.notification.api)
|
||||
api(projects.mail.common)
|
||||
|
||||
implementation(projects.core.common)
|
||||
implementation(projects.core.preference.api)
|
||||
|
||||
implementation(projects.feature.mail.account.api)
|
||||
implementation(projects.feature.mail.folder.api)
|
||||
|
||||
implementation(projects.backend.api)
|
||||
|
||||
testImplementation(projects.feature.account.fake)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import net.thunderbird.core.preference.storage.Storage
|
||||
|
||||
interface AccountDefaultsProvider {
|
||||
/**
|
||||
* Apply default values to the account.
|
||||
*
|
||||
* This method should only be called when creating a new account.
|
||||
*/
|
||||
fun applyDefaults(account: LegacyAccount)
|
||||
|
||||
/**
|
||||
* Apply any additional default values to the account.
|
||||
*
|
||||
* This method should be called when updating an existing account.
|
||||
*/
|
||||
fun applyOverwrites(account: LegacyAccount, storage: Storage)
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_MAXIMUM_AUTO_DOWNLOAD_MESSAGE_SIZE = 131072
|
||||
|
||||
@JvmStatic
|
||||
val DEFAULT_MESSAGE_FORMAT = MessageFormat.HTML
|
||||
|
||||
const val DEFAULT_MESSAGE_FORMAT_AUTO = false
|
||||
const val DEFAULT_MESSAGE_READ_RECEIPT = false
|
||||
const val DEFAULT_QUOTED_TEXT_SHOWN = true
|
||||
const val DEFAULT_QUOTE_PREFIX = ">"
|
||||
|
||||
@JvmStatic
|
||||
val DEFAULT_QUOTE_STYLE = QuoteStyle.PREFIX
|
||||
|
||||
const val DEFAULT_REMOTE_SEARCH_NUM_RESULTS = 25
|
||||
const val DEFAULT_REPLY_AFTER_QUOTE = false
|
||||
const val DEFAULT_RINGTONE_URI = "content://settings/system/notification_sound"
|
||||
const val DEFAULT_SORT_ASCENDING = false
|
||||
|
||||
@JvmStatic
|
||||
val DEFAULT_SORT_TYPE = SortType.SORT_DATE
|
||||
|
||||
const val DEFAULT_STRIP_SIGNATURE = true
|
||||
|
||||
const val DEFAULT_SYNC_INTERVAL = 60
|
||||
|
||||
/**
|
||||
* Specifies how many messages will be shown in a folder by default. This number is set
|
||||
* on each new folder and can be incremented with "Load more messages..." by the
|
||||
* VISIBLE_LIMIT_INCREMENT
|
||||
*/
|
||||
const val DEFAULT_VISIBLE_LIMIT = 25
|
||||
|
||||
const val NO_OPENPGP_KEY: Long = 0
|
||||
|
||||
const val UNASSIGNED_ACCOUNT_NUMBER = -1
|
||||
|
||||
// TODO : Remove once storage is migrated to new format
|
||||
const val COLOR = 0x0099CC
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import net.thunderbird.feature.mail.account.api.AccountManager
|
||||
|
||||
@Deprecated(
|
||||
message = "Use net.thunderbird.feature.mail.account.api.AccountManager<TAccount : BaseAccount> instead",
|
||||
replaceWith = ReplaceWith(
|
||||
expression = "AccountManager<LegacyAccount>",
|
||||
"net.thunderbird.feature.mail.account.api.AccountManager",
|
||||
"app.k9mail.legacy.account.LegacyAccount",
|
||||
),
|
||||
)
|
||||
interface AccountManager : AccountManager<LegacyAccount> {
|
||||
override fun getAccounts(): List<LegacyAccount>
|
||||
override fun getAccountsFlow(): Flow<List<LegacyAccount>>
|
||||
override fun getAccount(accountUuid: String): LegacyAccount?
|
||||
override fun getAccountFlow(accountUuid: String): Flow<LegacyAccount?>
|
||||
fun addAccountRemovedListener(listener: AccountRemovedListener)
|
||||
override fun moveAccount(account: LegacyAccount, newPosition: Int)
|
||||
fun addOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener)
|
||||
fun removeOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener)
|
||||
override fun saveAccount(account: LegacyAccount)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
fun interface AccountRemovedListener {
|
||||
fun onAccountRemoved(account: LegacyAccount)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
fun interface AccountsChangeListener {
|
||||
fun onAccountsChanged()
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
enum class DeletePolicy(@JvmField val setting: Int) {
|
||||
NEVER(0),
|
||||
SEVEN_DAYS(1),
|
||||
ON_DELETE(2),
|
||||
MARK_AS_READ(3),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromInt(initialSetting: Int): DeletePolicy {
|
||||
return entries.find { it.setting == initialSetting } ?: error("DeletePolicy $initialSetting unknown")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import com.fsck.k9.backend.api.SyncConfig.ExpungePolicy
|
||||
|
||||
enum class Expunge {
|
||||
EXPUNGE_IMMEDIATELY,
|
||||
EXPUNGE_MANUALLY,
|
||||
EXPUNGE_ON_POLL,
|
||||
;
|
||||
|
||||
fun toBackendExpungePolicy(): ExpungePolicy = when (this) {
|
||||
EXPUNGE_IMMEDIATELY -> ExpungePolicy.IMMEDIATELY
|
||||
EXPUNGE_MANUALLY -> ExpungePolicy.MANUALLY
|
||||
EXPUNGE_ON_POLL -> ExpungePolicy.ON_POLL
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
enum class FolderMode {
|
||||
NONE,
|
||||
ALL,
|
||||
FIRST_CLASS,
|
||||
FIRST_AND_SECOND_CLASS,
|
||||
NOT_SECOND_CLASS,
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Identity(
|
||||
val description: String? = null,
|
||||
val name: String? = null,
|
||||
val email: String? = null,
|
||||
val signature: String? = null,
|
||||
val signatureUse: Boolean = false,
|
||||
val replyTo: String? = null,
|
||||
) : Parcelable {
|
||||
// TODO remove when callers are converted to Kotlin
|
||||
fun withName(name: String?) = copy(name = name)
|
||||
fun withSignature(signature: String?) = copy(signature = signature)
|
||||
fun withSignatureUse(signatureUse: Boolean) = copy(signatureUse = signatureUse)
|
||||
fun withEmail(email: String?) = copy(email = email)
|
||||
}
|
||||
|
|
@ -0,0 +1,636 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import com.fsck.k9.mail.Address
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import net.thunderbird.core.android.account.AccountDefaultsProvider.Companion.NO_OPENPGP_KEY
|
||||
import net.thunderbird.feature.account.Account
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
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.mail.account.api.BaseAccount
|
||||
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
|
||||
import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection
|
||||
import net.thunderbird.feature.notification.NotificationSettings
|
||||
|
||||
// This needs to be in sync with K9.DEFAULT_VISIBLE_LIMIT
|
||||
const val DEFAULT_VISIBLE_LIMIT = 25
|
||||
|
||||
/**
|
||||
* Account stores all of the settings for a single account defined by the user. Each account is defined by a UUID.
|
||||
*/
|
||||
@Deprecated("Use LegacyAccountWrapper instead")
|
||||
@Suppress("TooManyFunctions")
|
||||
open class LegacyAccount(
|
||||
override val uuid: String,
|
||||
val isSensitiveDebugLoggingEnabled: () -> Boolean = { false },
|
||||
) : Account, BaseAccount {
|
||||
|
||||
// [Account]
|
||||
override val id: AccountId = AccountIdFactory.of(uuid)
|
||||
|
||||
// [BaseAccount]
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
override var name: String? = null
|
||||
set(value) {
|
||||
field = value?.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
override var email: String
|
||||
get() = identities[0].email!!
|
||||
set(email) {
|
||||
val newIdentity = identities[0].copy(email = email)
|
||||
identities[0] = newIdentity
|
||||
}
|
||||
|
||||
// [AccountProfile]
|
||||
val displayName: String
|
||||
get() = name ?: email
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var chipColor = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var avatar: AvatarDto = AvatarDto(
|
||||
avatarType = AvatarTypeDto.MONOGRAM,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = null,
|
||||
avatarIconName = null,
|
||||
)
|
||||
|
||||
// Uncategorized
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var deletePolicy = DeletePolicy.NEVER
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
private var internalIncomingServerSettings: ServerSettings? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
private var internalOutgoingServerSettings: ServerSettings? = null
|
||||
|
||||
var incomingServerSettings: ServerSettings
|
||||
get() = internalIncomingServerSettings ?: error("Incoming server settings not set yet")
|
||||
set(value) {
|
||||
internalIncomingServerSettings = value
|
||||
}
|
||||
|
||||
var outgoingServerSettings: ServerSettings
|
||||
get() = internalOutgoingServerSettings ?: error("Outgoing server settings not set yet")
|
||||
set(value) {
|
||||
internalOutgoingServerSettings = value
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var oAuthState: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var alwaysBcc: String? = null
|
||||
|
||||
/**
|
||||
* -1 for never.
|
||||
*/
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var automaticCheckIntervalMinutes = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var displayCount = 0
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value.takeIf { it != -1 } ?: DEFAULT_VISIBLE_LIMIT
|
||||
isChangedVisibleLimits = true
|
||||
}
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isNotifyNewMail = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var folderNotifyNewMailMode = FolderMode.ALL
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isNotifySelfNewMail = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isNotifyContactsMailOnly = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isIgnoreChatMessages = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var legacyInboxFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var importedDraftsFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var importedSentFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var importedTrashFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var importedArchiveFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var importedSpamFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var inboxFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var draftsFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var sentFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var trashFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var archiveFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var spamFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
var draftsFolderSelection = SpecialFolderSelection.AUTOMATIC
|
||||
|
||||
@get:Synchronized
|
||||
var sentFolderSelection = SpecialFolderSelection.AUTOMATIC
|
||||
|
||||
@get:Synchronized
|
||||
var trashFolderSelection = SpecialFolderSelection.AUTOMATIC
|
||||
|
||||
@get:Synchronized
|
||||
var archiveFolderSelection = SpecialFolderSelection.AUTOMATIC
|
||||
|
||||
@get:Synchronized
|
||||
var spamFolderSelection = SpecialFolderSelection.AUTOMATIC
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var importedAutoExpandFolder: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var autoExpandFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var folderDisplayMode = FolderMode.NOT_SECOND_CLASS
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var folderSyncMode = FolderMode.FIRST_CLASS
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var folderPushMode = FolderMode.NONE
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var accountNumber = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isNotifySync = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var sortType: SortType = SortType.SORT_DATE
|
||||
|
||||
var sortAscending: MutableMap<SortType, Boolean> = mutableMapOf()
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var showPictures = ShowPictures.NEVER
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isSignatureBeforeQuotedText = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var expungePolicy = Expunge.EXPUNGE_IMMEDIATELY
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var maxPushFolders = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var idleRefreshMinutes = 0
|
||||
|
||||
@get:JvmName("useCompression")
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var useCompression = true
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isSendClientInfoEnabled = true
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isSubscribedFoldersOnly = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var maximumPolledMessageAge = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var maximumAutoDownloadMessageSize = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var messageFormat = MessageFormat.HTML
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isMessageFormatAuto = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isMessageReadReceipt = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var quoteStyle = QuoteStyle.PREFIX
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var quotePrefix: String? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isDefaultQuotedTextShown = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isReplyAfterQuote = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isStripSignature = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isSyncRemoteDeletions = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var openPgpProvider: String? = null
|
||||
set(value) {
|
||||
field = value?.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var openPgpKey: Long = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var autocryptPreferEncryptMutual = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isOpenPgpHideSignOnly = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isOpenPgpEncryptSubject = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isOpenPgpEncryptAllDrafts = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isMarkMessageAsReadOnView = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isMarkMessageAsReadOnDelete = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isAlwaysShowCcBcc = false
|
||||
|
||||
// Temporarily disabled
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isRemoteSearchFullText = false
|
||||
get() = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var remoteSearchNumResults = 0
|
||||
set(value) {
|
||||
field = value.coerceAtLeast(0)
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isUploadSentMessages = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var lastSyncTime: Long = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var lastFolderListRefreshTime: Long = 0
|
||||
|
||||
@get:Synchronized
|
||||
var isFinishedSetup = false
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var messagesNotificationChannelVersion = 0
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var isChangedVisibleLimits = false
|
||||
|
||||
/**
|
||||
* Database ID of the folder that was last selected for a copy or move operation.
|
||||
*
|
||||
* Note: For now this value isn't persisted. So it will be reset when K-9 Mail is restarted.
|
||||
*/
|
||||
@get:Synchronized
|
||||
var lastSelectedFolderId: Long? = null
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var identities: MutableList<Identity> = mutableListOf()
|
||||
set(value) {
|
||||
field = value.toMutableList()
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
var notificationSettings = NotificationSettings()
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var senderName: String?
|
||||
get() = identities[0].name
|
||||
set(name) {
|
||||
val newIdentity = identities[0].withName(name)
|
||||
identities[0] = newIdentity
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var signatureUse: Boolean
|
||||
get() = identities[0].signatureUse
|
||||
set(signatureUse) {
|
||||
val newIdentity = identities[0].withSignatureUse(signatureUse)
|
||||
identities[0] = newIdentity
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var signature: String?
|
||||
get() = identities[0].signature
|
||||
set(signature) {
|
||||
val newIdentity = identities[0].withSignature(signature)
|
||||
identities[0] = newIdentity
|
||||
}
|
||||
|
||||
@get:JvmName("shouldMigrateToOAuth")
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var shouldMigrateToOAuth = false
|
||||
|
||||
@get:JvmName("folderPathDelimiter")
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var folderPathDelimiter: FolderPathDelimiter = "/"
|
||||
|
||||
/**
|
||||
* @param automaticCheckIntervalMinutes or -1 for never.
|
||||
*/
|
||||
@Synchronized
|
||||
fun updateAutomaticCheckIntervalMinutes(automaticCheckIntervalMinutes: Int): Boolean {
|
||||
val oldInterval = this.automaticCheckIntervalMinutes
|
||||
this.automaticCheckIntervalMinutes = automaticCheckIntervalMinutes
|
||||
|
||||
return oldInterval != automaticCheckIntervalMinutes
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setDraftsFolderId(folderId: Long?, selection: SpecialFolderSelection) {
|
||||
draftsFolderId = folderId
|
||||
draftsFolderSelection = selection
|
||||
}
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
@Synchronized
|
||||
fun hasDraftsFolder(): Boolean {
|
||||
return draftsFolderId != null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setSentFolderId(folderId: Long?, selection: SpecialFolderSelection) {
|
||||
sentFolderId = folderId
|
||||
sentFolderSelection = selection
|
||||
}
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
@Synchronized
|
||||
fun hasSentFolder(): Boolean {
|
||||
return sentFolderId != null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setTrashFolderId(folderId: Long?, selection: SpecialFolderSelection) {
|
||||
trashFolderId = folderId
|
||||
trashFolderSelection = selection
|
||||
}
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
@Synchronized
|
||||
fun hasTrashFolder(): Boolean {
|
||||
return trashFolderId != null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setArchiveFolderId(folderId: Long?, selection: SpecialFolderSelection) {
|
||||
archiveFolderId = folderId
|
||||
archiveFolderSelection = selection
|
||||
}
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
@Synchronized
|
||||
fun hasArchiveFolder(): Boolean {
|
||||
return archiveFolderId != null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setSpamFolderId(folderId: Long?, selection: SpecialFolderSelection) {
|
||||
spamFolderId = folderId
|
||||
spamFolderSelection = selection
|
||||
}
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
@Synchronized
|
||||
fun hasSpamFolder(): Boolean {
|
||||
return spamFolderId != null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun isSortAscending(sortType: SortType): Boolean {
|
||||
return sortAscending.getOrPut(sortType) { sortType.isDefaultAscending }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setSortAscending(sortType: SortType, sortAscending: Boolean) {
|
||||
this.sortAscending[sortType] = sortAscending
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun replaceIdentities(identities: List<Identity>) {
|
||||
this.identities = identities.toMutableList()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getIdentity(index: Int): Identity {
|
||||
if (index !in identities.indices) error("Identity with index $index not found")
|
||||
|
||||
return identities[index]
|
||||
}
|
||||
|
||||
fun isAnIdentity(addresses: Array<Address>?): Boolean {
|
||||
if (addresses == null) return false
|
||||
|
||||
return addresses.any { address -> isAnIdentity(address) }
|
||||
}
|
||||
|
||||
fun isAnIdentity(address: Address): Boolean {
|
||||
return findIdentity(address) != null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun findIdentity(address: Address): Identity? {
|
||||
return identities.find { identity ->
|
||||
identity.email.equals(address.address, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
val earliestPollDate: Date?
|
||||
get() {
|
||||
val age = maximumPolledMessageAge.takeIf { it >= 0 } ?: return null
|
||||
|
||||
val now = Calendar.getInstance()
|
||||
now[Calendar.HOUR_OF_DAY] = 0
|
||||
now[Calendar.MINUTE] = 0
|
||||
now[Calendar.SECOND] = 0
|
||||
now[Calendar.MILLISECOND] = 0
|
||||
|
||||
if (age < 28) {
|
||||
now.add(Calendar.DATE, age * -1)
|
||||
} else {
|
||||
when (age) {
|
||||
28 -> now.add(Calendar.MONTH, -1)
|
||||
56 -> now.add(Calendar.MONTH, -2)
|
||||
84 -> now.add(Calendar.MONTH, -3)
|
||||
168 -> now.add(Calendar.MONTH, -6)
|
||||
365 -> now.add(Calendar.YEAR, -1)
|
||||
}
|
||||
}
|
||||
|
||||
return now.time
|
||||
}
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
val isOpenPgpProviderConfigured: Boolean
|
||||
get() = openPgpProvider != null
|
||||
|
||||
@Deprecated("use AccountWrapper instead")
|
||||
@Synchronized
|
||||
fun hasOpenPgpKey(): Boolean {
|
||||
return openPgpKey != NO_OPENPGP_KEY
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun setLastSelectedFolderId(folderId: Long) {
|
||||
lastSelectedFolderId = folderId
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun resetChangeMarkers() {
|
||||
isChangedVisibleLimits = false
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun markSetupFinished() {
|
||||
isFinishedSetup = true
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateNotificationSettings(
|
||||
block: (
|
||||
oldNotificationSettings: NotificationSettings,
|
||||
) -> NotificationSettings,
|
||||
) {
|
||||
notificationSettings = block(notificationSettings)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return if (isSensitiveDebugLoggingEnabled()) displayName else uuid
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return if (other is LegacyAccount) {
|
||||
other.uuid == uuid
|
||||
} else {
|
||||
super.equals(other)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return uuid.hashCode()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Fixed name of outbox - not actually displayed.
|
||||
*/
|
||||
const val OUTBOX_NAME = "Outbox"
|
||||
|
||||
const val INTERVAL_MINUTES_NEVER = -1
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import net.thunderbird.core.android.account.AccountDefaultsProvider.Companion.NO_OPENPGP_KEY
|
||||
import net.thunderbird.core.common.mail.Protocols
|
||||
import net.thunderbird.feature.account.Account
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
import net.thunderbird.feature.account.storage.profile.ProfileDto
|
||||
import net.thunderbird.feature.mail.account.api.BaseAccount
|
||||
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
|
||||
import net.thunderbird.feature.mail.folder.api.SpecialFolderSelection
|
||||
import net.thunderbird.feature.notification.NotificationSettings
|
||||
|
||||
/**
|
||||
* A immutable wrapper for the [LegacyAccount] class.
|
||||
*
|
||||
* This class is used to store the account data in a way that is safe to pass between threads.
|
||||
*
|
||||
* Use LegacyAccountWrapper.from(account) to create a wrapper from an account.
|
||||
* Use LegacyAccountWrapper.to(wrapper) to create an account from a wrapper.
|
||||
*/
|
||||
data class LegacyAccountWrapper(
|
||||
val isSensitiveDebugLoggingEnabled: () -> Boolean = { false },
|
||||
|
||||
// [Account]
|
||||
override val id: AccountId,
|
||||
|
||||
// [BaseAccount]
|
||||
override val name: String?,
|
||||
override val email: String,
|
||||
|
||||
// [AccountProfile]
|
||||
val profile: ProfileDto,
|
||||
|
||||
// Uncategorized
|
||||
val deletePolicy: DeletePolicy = DeletePolicy.NEVER,
|
||||
val incomingServerSettings: ServerSettings,
|
||||
val outgoingServerSettings: ServerSettings,
|
||||
val oAuthState: String? = null,
|
||||
val alwaysBcc: String? = null,
|
||||
val automaticCheckIntervalMinutes: Int = 0,
|
||||
val displayCount: Int = 0,
|
||||
val isNotifyNewMail: Boolean = false,
|
||||
val folderNotifyNewMailMode: FolderMode = FolderMode.ALL,
|
||||
val isNotifySelfNewMail: Boolean = false,
|
||||
val isNotifyContactsMailOnly: Boolean = false,
|
||||
val isIgnoreChatMessages: Boolean = false,
|
||||
val legacyInboxFolder: String? = null,
|
||||
val importedDraftsFolder: String? = null,
|
||||
val importedSentFolder: String? = null,
|
||||
val importedTrashFolder: String? = null,
|
||||
val importedArchiveFolder: String? = null,
|
||||
val importedSpamFolder: String? = null,
|
||||
val inboxFolderId: Long? = null,
|
||||
val draftsFolderId: Long? = null,
|
||||
val sentFolderId: Long? = null,
|
||||
val trashFolderId: Long? = null,
|
||||
val archiveFolderId: Long? = null,
|
||||
val spamFolderId: Long? = null,
|
||||
val draftsFolderSelection: SpecialFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
val sentFolderSelection: SpecialFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
val trashFolderSelection: SpecialFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
val archiveFolderSelection: SpecialFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
val spamFolderSelection: SpecialFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
val importedAutoExpandFolder: String? = null,
|
||||
val autoExpandFolderId: Long? = null,
|
||||
val folderDisplayMode: FolderMode = FolderMode.NOT_SECOND_CLASS,
|
||||
val folderSyncMode: FolderMode = FolderMode.FIRST_CLASS,
|
||||
val folderPushMode: FolderMode = FolderMode.NONE,
|
||||
val accountNumber: Int = 0,
|
||||
val isNotifySync: Boolean = false,
|
||||
val sortType: SortType = SortType.SORT_DATE,
|
||||
val sortAscending: Map<SortType, Boolean> = emptyMap(),
|
||||
val showPictures: ShowPictures = ShowPictures.NEVER,
|
||||
val isSignatureBeforeQuotedText: Boolean = false,
|
||||
val expungePolicy: Expunge = Expunge.EXPUNGE_IMMEDIATELY,
|
||||
val maxPushFolders: Int = 0,
|
||||
val idleRefreshMinutes: Int = 0,
|
||||
val useCompression: Boolean = true,
|
||||
val isSendClientInfoEnabled: Boolean = true,
|
||||
val isSubscribedFoldersOnly: Boolean = false,
|
||||
val maximumPolledMessageAge: Int = 0,
|
||||
val maximumAutoDownloadMessageSize: Int = 0,
|
||||
val messageFormat: MessageFormat = MessageFormat.HTML,
|
||||
val isMessageFormatAuto: Boolean = false,
|
||||
val isMessageReadReceipt: Boolean = false,
|
||||
val quoteStyle: QuoteStyle = QuoteStyle.PREFIX,
|
||||
val quotePrefix: String? = null,
|
||||
val isDefaultQuotedTextShown: Boolean = false,
|
||||
val isReplyAfterQuote: Boolean = false,
|
||||
val isStripSignature: Boolean = false,
|
||||
val isSyncRemoteDeletions: Boolean = false,
|
||||
val openPgpProvider: String? = null,
|
||||
val openPgpKey: Long = 0,
|
||||
val autocryptPreferEncryptMutual: Boolean = false,
|
||||
val isOpenPgpHideSignOnly: Boolean = false,
|
||||
val isOpenPgpEncryptSubject: Boolean = false,
|
||||
val isOpenPgpEncryptAllDrafts: Boolean = false,
|
||||
val isMarkMessageAsReadOnView: Boolean = false,
|
||||
val isMarkMessageAsReadOnDelete: Boolean = false,
|
||||
val isAlwaysShowCcBcc: Boolean = false,
|
||||
val isRemoteSearchFullText: Boolean = false,
|
||||
val remoteSearchNumResults: Int = 0,
|
||||
val isUploadSentMessages: Boolean = false,
|
||||
val lastSyncTime: Long = 0,
|
||||
val lastFolderListRefreshTime: Long = 0,
|
||||
val isFinishedSetup: Boolean = false,
|
||||
val messagesNotificationChannelVersion: Int = 0,
|
||||
val isChangedVisibleLimits: Boolean = false,
|
||||
val lastSelectedFolderId: Long? = null,
|
||||
val identities: List<Identity>,
|
||||
val notificationSettings: NotificationSettings = NotificationSettings(),
|
||||
val senderName: String? = identities[0].name,
|
||||
val signatureUse: Boolean = identities[0].signatureUse,
|
||||
val signature: String? = identities[0].signature,
|
||||
val shouldMigrateToOAuth: Boolean = false,
|
||||
val folderPathDelimiter: FolderPathDelimiter = "/",
|
||||
) : Account, BaseAccount {
|
||||
|
||||
override val uuid: String = id.asRaw()
|
||||
|
||||
fun hasDraftsFolder(): Boolean {
|
||||
return draftsFolderId != null
|
||||
}
|
||||
|
||||
fun hasSentFolder(): Boolean {
|
||||
return sentFolderId != null
|
||||
}
|
||||
|
||||
fun hasTrashFolder(): Boolean {
|
||||
return trashFolderId != null
|
||||
}
|
||||
|
||||
fun hasArchiveFolder(): Boolean {
|
||||
return archiveFolderId != null
|
||||
}
|
||||
|
||||
fun hasSpamFolder(): Boolean {
|
||||
return spamFolderId != null
|
||||
}
|
||||
|
||||
fun isOpenPgpProviderConfigured(): Boolean {
|
||||
return openPgpProvider != null
|
||||
}
|
||||
|
||||
fun hasOpenPgpKey(): Boolean {
|
||||
return openPgpKey != NO_OPENPGP_KEY
|
||||
}
|
||||
|
||||
fun isIncomingServerPop3(): Boolean =
|
||||
incomingServerSettings.type == Protocols.POP3
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import net.thunderbird.feature.account.AccountId
|
||||
|
||||
interface LegacyAccountWrapperManager {
|
||||
fun getAll(): Flow<List<LegacyAccountWrapper>>
|
||||
|
||||
fun getById(id: AccountId): Flow<LegacyAccountWrapper?>
|
||||
|
||||
suspend fun update(account: LegacyAccountWrapper)
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
enum class MessageFormat {
|
||||
TEXT,
|
||||
HTML,
|
||||
AUTO,
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
enum class QuoteStyle {
|
||||
PREFIX,
|
||||
HEADER,
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
enum class ShowPictures {
|
||||
NEVER,
|
||||
ALWAYS,
|
||||
ONLY_FROM_CONTACTS,
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
enum class SortType(val isDefaultAscending: Boolean) {
|
||||
SORT_DATE(false),
|
||||
SORT_ARRIVAL(false),
|
||||
SORT_SUBJECT(true),
|
||||
SORT_SENDER(true),
|
||||
SORT_UNREAD(true),
|
||||
SORT_FLAGGED(true),
|
||||
SORT_ATTACHMENT(true),
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
package net.thunderbird.core.android.account
|
||||
|
||||
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
|
||||
import net.thunderbird.account.fake.FakeAccountProfileData.PROFILE_COLOR
|
||||
import net.thunderbird.account.fake.FakeAccountProfileData.PROFILE_NAME
|
||||
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 LegacyAccountWrapperTest {
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Test
|
||||
fun `should set defaults`() {
|
||||
// arrange
|
||||
val expected = createAccountWrapper()
|
||||
|
||||
// act
|
||||
val result = LegacyAccountWrapper(
|
||||
isSensitiveDebugLoggingEnabled = isSensitiveDebugLoggingEnabled,
|
||||
id = ACCOUNT_ID,
|
||||
name = PROFILE_NAME,
|
||||
email = email,
|
||||
profile = profile,
|
||||
incomingServerSettings = incomingServerSettings,
|
||||
outgoingServerSettings = outgoingServerSettings,
|
||||
identities = identities,
|
||||
)
|
||||
|
||||
// assert
|
||||
assertThat(expected).isEqualTo(result)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val isSensitiveDebugLoggingEnabled = { true }
|
||||
|
||||
const val email = "demo@example.com"
|
||||
|
||||
val avatar = AvatarDto(
|
||||
avatarType = AvatarTypeDto.MONOGRAM,
|
||||
avatarMonogram = null,
|
||||
avatarImageUri = null,
|
||||
avatarIconName = null,
|
||||
)
|
||||
|
||||
val profile = ProfileDto(
|
||||
id = ACCOUNT_ID,
|
||||
name = PROFILE_NAME,
|
||||
color = PROFILE_COLOR,
|
||||
avatar = avatar,
|
||||
)
|
||||
|
||||
val incomingServerSettings = ServerSettings(
|
||||
type = "imap",
|
||||
host = "imap.example.com",
|
||||
port = 993,
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "test",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
)
|
||||
|
||||
val outgoingServerSettings = ServerSettings(
|
||||
type = "smtp",
|
||||
host = "smtp.example.com",
|
||||
port = 465,
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "test",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
)
|
||||
|
||||
val identities = mutableListOf(
|
||||
Identity(
|
||||
email = "demo@example.com",
|
||||
name = "identityName",
|
||||
signatureUse = true,
|
||||
signature = "signature",
|
||||
description = "Demo User",
|
||||
),
|
||||
)
|
||||
|
||||
val notificationSettings = NotificationSettings()
|
||||
|
||||
@Suppress("LongMethod")
|
||||
fun createAccountWrapper(): LegacyAccountWrapper {
|
||||
return LegacyAccountWrapper(
|
||||
isSensitiveDebugLoggingEnabled = isSensitiveDebugLoggingEnabled,
|
||||
|
||||
// [Account]
|
||||
id = ACCOUNT_ID,
|
||||
|
||||
// [BaseAccount]
|
||||
name = PROFILE_NAME,
|
||||
email = email,
|
||||
|
||||
// [AccountProfile]
|
||||
profile = profile,
|
||||
|
||||
// Uncategorized
|
||||
deletePolicy = DeletePolicy.NEVER,
|
||||
incomingServerSettings = incomingServerSettings,
|
||||
outgoingServerSettings = outgoingServerSettings,
|
||||
oAuthState = null,
|
||||
alwaysBcc = null,
|
||||
automaticCheckIntervalMinutes = 0,
|
||||
displayCount = 0,
|
||||
isNotifyNewMail = false,
|
||||
folderNotifyNewMailMode = FolderMode.ALL,
|
||||
isNotifySelfNewMail = false,
|
||||
isNotifyContactsMailOnly = false,
|
||||
isIgnoreChatMessages = false,
|
||||
legacyInboxFolder = null,
|
||||
importedDraftsFolder = null,
|
||||
importedSentFolder = null,
|
||||
importedTrashFolder = null,
|
||||
importedArchiveFolder = null,
|
||||
importedSpamFolder = null,
|
||||
inboxFolderId = null,
|
||||
draftsFolderId = null,
|
||||
sentFolderId = null,
|
||||
trashFolderId = null,
|
||||
archiveFolderId = null,
|
||||
spamFolderId = null,
|
||||
draftsFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
sentFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
trashFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
archiveFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
spamFolderSelection = SpecialFolderSelection.AUTOMATIC,
|
||||
importedAutoExpandFolder = null,
|
||||
autoExpandFolderId = null,
|
||||
folderDisplayMode = FolderMode.NOT_SECOND_CLASS,
|
||||
folderSyncMode = FolderMode.FIRST_CLASS,
|
||||
folderPushMode = FolderMode.NONE,
|
||||
accountNumber = 0,
|
||||
isNotifySync = false,
|
||||
sortType = SortType.SORT_DATE,
|
||||
sortAscending = emptyMap(),
|
||||
showPictures = ShowPictures.NEVER,
|
||||
isSignatureBeforeQuotedText = false,
|
||||
expungePolicy = Expunge.EXPUNGE_IMMEDIATELY,
|
||||
maxPushFolders = 0,
|
||||
idleRefreshMinutes = 0,
|
||||
useCompression = true,
|
||||
isSendClientInfoEnabled = true,
|
||||
isSubscribedFoldersOnly = false,
|
||||
maximumPolledMessageAge = 0,
|
||||
maximumAutoDownloadMessageSize = 0,
|
||||
messageFormat = MessageFormat.HTML,
|
||||
isMessageFormatAuto = false,
|
||||
isMessageReadReceipt = false,
|
||||
quoteStyle = QuoteStyle.PREFIX,
|
||||
quotePrefix = null,
|
||||
isDefaultQuotedTextShown = false,
|
||||
isReplyAfterQuote = false,
|
||||
isStripSignature = false,
|
||||
isSyncRemoteDeletions = false,
|
||||
openPgpProvider = null,
|
||||
openPgpKey = 0,
|
||||
autocryptPreferEncryptMutual = false,
|
||||
isOpenPgpHideSignOnly = false,
|
||||
isOpenPgpEncryptSubject = false,
|
||||
isOpenPgpEncryptAllDrafts = false,
|
||||
isMarkMessageAsReadOnView = false,
|
||||
isMarkMessageAsReadOnDelete = false,
|
||||
isAlwaysShowCcBcc = false,
|
||||
isRemoteSearchFullText = false,
|
||||
remoteSearchNumResults = 0,
|
||||
isUploadSentMessages = false,
|
||||
lastSyncTime = 0,
|
||||
lastFolderListRefreshTime = 0,
|
||||
isFinishedSetup = false,
|
||||
messagesNotificationChannelVersion = 0,
|
||||
isChangedVisibleLimits = false,
|
||||
lastSelectedFolderId = null,
|
||||
identities = identities,
|
||||
notificationSettings = notificationSettings,
|
||||
senderName = identities[0].name,
|
||||
signatureUse = identities[0].signatureUse,
|
||||
signature = identities[0].signature,
|
||||
shouldMigrateToOAuth = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue