Format Kotlin code

This commit is contained in:
cketti 2020-10-16 16:30:51 +02:00
parent 10be88d3e1
commit 1873593dc5
112 changed files with 1387 additions and 991 deletions

View file

@ -30,40 +30,44 @@ class ProvidersXmlDiscovery(
val emailUrlEncoded = UrlEncodingHelper.encodeUtf8(email)
val incomingUserUrlEncoded = provider.incomingUsernameTemplate
.replace("\$email", emailUrlEncoded)
.replace("\$user", userUrlEncoded)
.replace("\$domain", domain)
.replace("\$email", emailUrlEncoded)
.replace("\$user", userUrlEncoded)
.replace("\$domain", domain)
val incomingUri = with(URI(provider.incomingUriTemplate)) {
URI(scheme, "$incomingUserUrlEncoded:$password", host, port, null, null, null).toString()
}
val incomingSettings = backendManager.decodeStoreUri(incomingUri).let {
listOf(DiscoveredServerSettings(
it.type,
it.host,
it.port,
it.connectionSecurity,
it.authenticationType,
it.username
))
listOf(
DiscoveredServerSettings(
it.type,
it.host,
it.port,
it.connectionSecurity,
it.authenticationType,
it.username
)
)
}
val outgoingUserUrlEncoded = provider.outgoingUsernameTemplate
?.replace("\$email", emailUrlEncoded)
?.replace("\$user", userUrlEncoded)
?.replace("\$domain", domain)
?.replace("\$email", emailUrlEncoded)
?.replace("\$user", userUrlEncoded)
?.replace("\$domain", domain)
val outgoingUserInfo = if (outgoingUserUrlEncoded != null) "$outgoingUserUrlEncoded:$password" else null
val outgoingUri = with(URI(provider.outgoingUriTemplate)) {
URI(scheme, outgoingUserInfo, host, port, null, null, null).toString()
}
val outgoingSettings = backendManager.decodeTransportUri(outgoingUri).let {
listOf(DiscoveredServerSettings(
it.type,
it.host,
it.port,
it.connectionSecurity,
it.authenticationType,
it.username
))
listOf(
DiscoveredServerSettings(
it.type,
it.host,
it.port,
it.connectionSecurity,
it.authenticationType,
it.username
)
)
}
return DiscoveryResults(incomingSettings, outgoingSettings)
@ -120,8 +124,7 @@ class ProvidersXmlDiscovery(
}
} while (!(xmlEventType == XmlPullParser.END_TAG && xml.name == "provider"))
return if (incomingUriTemplate != null && incomingUsernameTemplate != null &&
outgoingUriTemplate != null) {
return if (incomingUriTemplate != null && incomingUsernameTemplate != null && outgoingUriTemplate != null) {
Provider(incomingUriTemplate, incomingUsernameTemplate, outgoingUriTemplate, outgoingUsernameTemplate)
} else {
null

View file

@ -46,7 +46,8 @@ class ProvidersXmlDiscoveryTest : RobolectricTest() {
@Test
fun discover_withUnknownDomain_shouldReturnNull() {
val connectionSettings = providersXmlDiscovery.discover(
"user@not.present.in.providers.xml.example", DiscoveryTarget.INCOMING_AND_OUTGOING)
"user@not.present.in.providers.xml.example", DiscoveryTarget.INCOMING_AND_OUTGOING
)
assertThat(connectionSettings).isNull()
}

View file

@ -28,8 +28,9 @@ class SrvServiceDiscoveryTest : RobolectricTest() {
fun discover_whenNoSMTP_shouldReturnJustIMAP() {
val srvResolver = newMockSrvResolver(
imapServices = listOf(newMailService(port = 143, srvType = SrvType.IMAP)),
imapsServices = listOf(newMailService(port = 993, srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED))
imapsServices = listOf(
newMailService(port = 993, srvType = SrvType.IMAPS, security = ConnectionSecurity.SSL_TLS_REQUIRED)
)
)
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
@ -41,10 +42,20 @@ class SrvServiceDiscoveryTest : RobolectricTest() {
@Test
fun discover_whenNoIMAP_shouldReturnJustSMTP() {
val srvResolver = newMockSrvResolver(submissionServices = listOf(
newMailService(port = 25, srvType = SrvType.SUBMISSION, security = ConnectionSecurity.STARTTLS_REQUIRED),
newMailService(port = 465, srvType = SrvType.SUBMISSIONS, security = ConnectionSecurity.SSL_TLS_REQUIRED)
))
val srvResolver = newMockSrvResolver(
submissionServices = listOf(
newMailService(
port = 25,
srvType = SrvType.SUBMISSION,
security = ConnectionSecurity.STARTTLS_REQUIRED
),
newMailService(
port = 465,
srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED
)
)
)
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
val result = srvServiceDiscovery.discover("test@example.com", DiscoveryTarget.INCOMING_AND_OUTGOING)
@ -55,64 +66,109 @@ class SrvServiceDiscoveryTest : RobolectricTest() {
@Test
fun discover_withRequiredServices_shouldCorrectlyPrioritize() {
val srvResolver = newMockSrvResolver(submissionServices = listOf(
newMailService(
host = "smtp1.example.com", port = 25, srvType = SrvType.SUBMISSION,
security = ConnectionSecurity.STARTTLS_REQUIRED, priority = 0),
newMailService(
host = "smtp2.example.com", port = 25, srvType = SrvType.SUBMISSION,
security = ConnectionSecurity.STARTTLS_REQUIRED, priority = 1)
), submissionsServices = listOf(
newMailService(
host = "smtp3.example.com", port = 465, srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED, priority = 0),
newMailService(
host = "smtp4.example.com", port = 465, srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED, priority = 1)
), imapServices = listOf(
newMailService(
host = "imap1.example.com", port = 143, srvType = SrvType.IMAP,
security = ConnectionSecurity.STARTTLS_REQUIRED, priority = 0),
newMailService(
host = "imap2.example.com", port = 143, srvType = SrvType.IMAP,
security = ConnectionSecurity.STARTTLS_REQUIRED, priority = 1)
), imapsServices = listOf(
newMailService(
host = "imaps1.example.com", port = 993, srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED, priority = 0),
newMailService(
host = "imaps2.example.com", port = 993, srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED, priority = 1)
))
val srvResolver = newMockSrvResolver(
submissionServices = listOf(
newMailService(
host = "smtp1.example.com",
port = 25,
srvType = SrvType.SUBMISSION,
security = ConnectionSecurity.STARTTLS_REQUIRED,
priority = 0
),
newMailService(
host = "smtp2.example.com",
port = 25,
srvType = SrvType.SUBMISSION,
security = ConnectionSecurity.STARTTLS_REQUIRED,
priority = 1
)
),
submissionsServices = listOf(
newMailService(
host = "smtp3.example.com",
port = 465,
srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
priority = 0
),
newMailService(
host = "smtp4.example.com",
port = 465,
srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
priority = 1
)
),
imapServices = listOf(
newMailService(
host = "imap1.example.com",
port = 143,
srvType = SrvType.IMAP,
security = ConnectionSecurity.STARTTLS_REQUIRED,
priority = 0
),
newMailService(
host = "imap2.example.com",
port = 143,
srvType = SrvType.IMAP,
security = ConnectionSecurity.STARTTLS_REQUIRED,
priority = 1
)
),
imapsServices = listOf(
newMailService(
host = "imaps1.example.com",
port = 993,
srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
priority = 0
),
newMailService(
host = "imaps2.example.com",
port = 993,
srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
priority = 1
)
)
)
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
val result = srvServiceDiscovery.discover("test@example.com", DiscoveryTarget.INCOMING_AND_OUTGOING)
assertEquals(listOf(
"smtp3.example.com",
"smtp1.example.com",
"smtp4.example.com",
"smtp2.example.com"
),
assertEquals(
listOf(
"smtp3.example.com",
"smtp1.example.com",
"smtp4.example.com",
"smtp2.example.com"
),
result?.outgoing?.map { it.host }
)
assertEquals(listOf(
"imaps1.example.com",
"imap1.example.com",
"imaps2.example.com",
"imap2.example.com"
),
assertEquals(
listOf(
"imaps1.example.com",
"imap1.example.com",
"imaps2.example.com",
"imap2.example.com"
),
result?.incoming?.map { it.host }
)
}
@Test
fun discover_whenOnlyOutgoingTrue_shouldOnlyFetchOutgoing() {
val srvResolver = newMockSrvResolver(submissionServices = listOf(
newMailService(
host = "smtp.example.com", port = 465, srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED, priority = 0)
))
val srvResolver = newMockSrvResolver(
submissionServices = listOf(
newMailService(
host = "smtp.example.com",
port = 465,
srvType = SrvType.SUBMISSIONS,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
priority = 0
)
)
)
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
val result = srvServiceDiscovery.discover("test@example.com", DiscoveryTarget.OUTGOING)
@ -126,11 +182,17 @@ class SrvServiceDiscoveryTest : RobolectricTest() {
@Test
fun discover_whenOnlyIncomingTrue_shouldOnlyFetchIncoming() {
val srvResolver = newMockSrvResolver(imapsServices = listOf(
newMailService(
host = "imaps.example.com", port = 993, srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED, priority = 0)
))
val srvResolver = newMockSrvResolver(
imapsServices = listOf(
newMailService(
host = "imaps.example.com",
port = 993,
srvType = SrvType.IMAPS,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
priority = 0
)
)
)
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
val result = srvServiceDiscovery.discover("test@example.com", DiscoveryTarget.INCOMING)

View file

@ -29,11 +29,11 @@ class ThunderbirdAutoconfigFetcher(private val okHttpClient: OkHttpClient) {
requireNotNull(domain) { "Couldn't extract domain from email address: $email" }
return HttpUrl.Builder()
.scheme("https")
.host(domain)
.addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml")
.addQueryParameter("emailaddress", email)
.build()
.scheme("https")
.host(domain)
.addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml")
.addQueryParameter("emailaddress", email)
.build()
}
}
}

View file

@ -154,19 +154,29 @@ class ThunderbirdAutoconfigTest : RobolectricTest() {
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
assertThat(connectionSettings).isEqualTo(DiscoveryResults(listOf(
DiscoveredServerSettings(
protocol = "imap", host = "imap.googlemail.com", port = 993,
security = ConnectionSecurity.SSL_TLS_REQUIRED, authType = AuthType.PLAIN,
username = "test@metacode.biz")
), listOf()))
assertThat(connectionSettings).isEqualTo(
DiscoveryResults(
listOf(
DiscoveredServerSettings(
protocol = "imap",
host = "imap.googlemail.com",
port = 993,
security = ConnectionSecurity.SSL_TLS_REQUIRED,
authType = AuthType.PLAIN,
username = "test@metacode.biz"
)
),
listOf()
)
)
}
@Test
fun generatedUrls() {
val autoDiscoveryAddress = ThunderbirdAutoconfigFetcher.getAutodiscoveryAddress("test@metacode.biz")
assertThat(autoDiscoveryAddress.toString()).isEqualTo("https://metacode.biz/" +
".well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40metacode.biz")
assertThat(autoDiscoveryAddress.toString()).isEqualTo(
"https://metacode.biz/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40metacode.biz"
)
}
}

View file

@ -62,28 +62,38 @@ class AccountPreferenceSerializer(
outboxFolderId = storage.getString("$accountUuid.outboxFolderId", null)?.toLongOrNull()
val draftsFolderId = storage.getString("$accountUuid.draftsFolderId", null)?.toLongOrNull()
val draftsFolderSelection = getEnumStringPref<SpecialFolderSelection>(storage, "$accountUuid.draftsFolderSelection",
SpecialFolderSelection.AUTOMATIC)
val draftsFolderSelection = getEnumStringPref<SpecialFolderSelection>(
storage, "$accountUuid.draftsFolderSelection",
SpecialFolderSelection.AUTOMATIC
)
setDraftsFolderId(draftsFolderId, draftsFolderSelection)
val sentFolderId = storage.getString("$accountUuid.sentFolderId", null)?.toLongOrNull()
val sentFolderSelection = getEnumStringPref<SpecialFolderSelection>(storage, "$accountUuid.sentFolderSelection",
SpecialFolderSelection.AUTOMATIC)
val sentFolderSelection = getEnumStringPref<SpecialFolderSelection>(
storage, "$accountUuid.sentFolderSelection",
SpecialFolderSelection.AUTOMATIC
)
setSentFolderId(sentFolderId, sentFolderSelection)
val trashFolderId = storage.getString("$accountUuid.trashFolderId", null)?.toLongOrNull()
val trashFolderSelection = getEnumStringPref<SpecialFolderSelection>(storage, "$accountUuid.trashFolderSelection",
SpecialFolderSelection.AUTOMATIC)
val trashFolderSelection = getEnumStringPref<SpecialFolderSelection>(
storage, "$accountUuid.trashFolderSelection",
SpecialFolderSelection.AUTOMATIC
)
setTrashFolderId(trashFolderId, trashFolderSelection)
val archiveFolderId = storage.getString("$accountUuid.archiveFolderId", null)?.toLongOrNull()
val archiveFolderSelection = getEnumStringPref<SpecialFolderSelection>(storage, "$accountUuid.archiveFolderSelection",
SpecialFolderSelection.AUTOMATIC)
val archiveFolderSelection = getEnumStringPref<SpecialFolderSelection>(
storage, "$accountUuid.archiveFolderSelection",
SpecialFolderSelection.AUTOMATIC
)
setArchiveFolderId(archiveFolderId, archiveFolderSelection)
val spamFolderId = storage.getString("$accountUuid.spamFolderId", null)?.toLongOrNull()
val spamFolderSelection = getEnumStringPref<SpecialFolderSelection>(storage, "$accountUuid.spamFolderSelection",
SpecialFolderSelection.AUTOMATIC)
val spamFolderSelection = getEnumStringPref<SpecialFolderSelection>(
storage, "$accountUuid.spamFolderSelection",
SpecialFolderSelection.AUTOMATIC
)
setSpamFolderId(spamFolderId, spamFolderSelection)
autoExpandFolderId = storage.getString("$accountUuid.autoExpandFolderId", null)?.toLongOrNull()
@ -108,8 +118,7 @@ class AccountPreferenceSerializer(
isReplyAfterQuote = storage.getBoolean("$accountUuid.replyAfterQuote", DEFAULT_REPLY_AFTER_QUOTE)
isStripSignature = storage.getBoolean("$accountUuid.stripSignature", DEFAULT_STRIP_SIGNATURE)
for (type in NetworkType.values()) {
val useCompression = storage.getBoolean("$accountUuid.useCompression.$type",
true)
val useCompression = storage.getBoolean("$accountUuid.useCompression.$type", true)
setCompression(type, useCompression)
}
@ -129,8 +138,10 @@ class AccountPreferenceSerializer(
notificationSetting.vibratePattern = storage.getInt("$accountUuid.vibratePattern", 0)
notificationSetting.vibrateTimes = storage.getInt("$accountUuid.vibrateTimes", 5)
notificationSetting.isRingEnabled = storage.getBoolean("$accountUuid.ring", true)
notificationSetting.ringtone = storage.getString("$accountUuid.ringtone",
"content://settings/system/notification_sound")
notificationSetting.ringtone = storage.getString(
"$accountUuid.ringtone",
"content://settings/system/notification_sound"
)
notificationSetting.setLed(storage.getBoolean("$accountUuid.led", true))
notificationSetting.ledColor = storage.getInt("$accountUuid.ledColor", chipColor)
@ -532,8 +543,10 @@ class AccountPreferenceSerializer(
try {
java.lang.Enum.valueOf<T>(defaultEnum.declaringClass, stringPref)
} catch (ex: IllegalArgumentException) {
Timber.w(ex, "Unable to convert preference key [%s] value [%s] to enum of type %s",
key, stringPref, defaultEnum.declaringClass)
Timber.w(
ex, "Unable to convert preference key [%s] value [%s] to enum of type %s",
key, stringPref, defaultEnum.declaringClass
)
defaultEnum
}
@ -634,6 +647,7 @@ class AccountPreferenceSerializer(
@JvmField
val DEFAULT_MESSAGE_FORMAT = MessageFormat.HTML
@JvmField
val DEFAULT_QUOTE_STYLE = QuoteStyle.PREFIX
const val DEFAULT_MESSAGE_FORMAT_AUTO = false

View file

@ -61,16 +61,17 @@ object Core : EarlyInit {
for (clazz in appConfig.componentsToDisable) {
val alreadyEnabled = pm.getComponentEnabledSetting(ComponentName(context, clazz)) ==
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
if (enabled != alreadyEnabled) {
pm.setComponentEnabledSetting(
ComponentName(context, clazz),
if (enabled)
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP)
ComponentName(context, clazz),
if (enabled)
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
}
}
@ -94,16 +95,19 @@ object Core : EarlyInit {
val queue = SynchronousQueue<Handler>()
// starting a new thread to handle unmount events
Thread(Runnable {
Looper.prepare()
try {
queue.put(Handler())
} catch (e: InterruptedException) {
Timber.e(e)
}
Thread(
Runnable {
Looper.prepare()
try {
queue.put(Handler())
} catch (e: InterruptedException) {
Timber.e(e)
}
Looper.loop()
}, "Unmount-thread").start()
Looper.loop()
},
"Unmount-thread"
).start()
try {
val storageGoneHandler = queue.take()

View file

@ -14,17 +14,17 @@ import com.fsck.k9.preferences.preferencesModule
import com.fsck.k9.search.searchModule
val coreModules = listOf(
mainModule,
openPgpModule,
autocryptModule,
mailStoreModule,
searchModule,
extractorModule,
htmlModule,
quoteModule,
coreNotificationModule,
controllerModule,
jobModule,
helperModule,
preferencesModule
mainModule,
openPgpModule,
autocryptModule,
mailStoreModule,
searchModule,
extractorModule,
htmlModule,
quoteModule,
coreNotificationModule,
controllerModule,
jobModule,
helperModule,
preferencesModule
)

View file

@ -8,8 +8,8 @@ import org.koin.core.context.startKoin
import org.koin.core.module.Module
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier
import org.koin.java.KoinJavaComponent.get as koinGet
import org.koin.java.KoinJavaComponent.getKoin
import org.koin.java.KoinJavaComponent.get as koinGet
object DI {
private const val DEBUG = false

View file

@ -20,8 +20,8 @@ class EmailAddressValidator {
private const val HOST_NAME = "((($DOMAIN_LABEL\\.)+$TOP_LABEL)|$DOMAIN_LABEL)"
private val EMAIL_ADDRESS_PATTERN = Pattern.compile(
"^($ATEXT+(\\.$ATEXT+)*|\"$QCONTENT+\")" +
"\\@$HOST_NAME"
"^($ATEXT+(\\.$ATEXT+)*|\"$QCONTENT+\")" +
"\\@$HOST_NAME"
)
}
}

View file

@ -124,9 +124,9 @@ object K9 : EarlyInit {
}
preferences.createStorageEditor()
.remove("openPgpProvider")
.remove("openPgpSupportSignOnly")
.commit()
.remove("openPgpProvider")
.remove("openPgpSupportSignOnly")
.commit()
}
@JvmStatic
@ -376,8 +376,10 @@ object K9 : EarlyInit {
notificationHideSubject = storage.getEnum("notificationHideSubject", NotificationHideSubject.NEVER)
notificationQuickDeleteBehaviour = storage.getEnum("notificationQuickDelete", NotificationQuickDelete.ALWAYS)
lockScreenNotificationVisibility = storage.getEnum("lockScreenNotificationVisibility",
LockScreenNotificationVisibility.MESSAGE_COUNT)
lockScreenNotificationVisibility = storage.getEnum(
"lockScreenNotificationVisibility",
LockScreenNotificationVisibility.MESSAGE_COUNT
)
splitViewMode = storage.getEnum("splitViewMode", SplitViewMode.NEVER)

View file

@ -48,11 +48,15 @@ data class AutocryptDraftStateHeader(
@JvmStatic
fun fromCryptoStatus(cryptoStatus: CryptoStatus): AutocryptDraftStateHeader {
if (cryptoStatus.isSignOnly) {
return AutocryptDraftStateHeader(false, true, cryptoStatus.isReplyToEncrypted,
cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf())
return AutocryptDraftStateHeader(
false, true, cryptoStatus.isReplyToEncrypted,
cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf()
)
}
return AutocryptDraftStateHeader(cryptoStatus.isEncryptionEnabled, false, cryptoStatus.isReplyToEncrypted,
cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf())
return AutocryptDraftStateHeader(
cryptoStatus.isEncryptionEnabled, false, cryptoStatus.isReplyToEncrypted,
cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf()
)
}
}
}

View file

@ -1,4 +1,5 @@
@file:JvmName("Preconditions")
package com.fsck.k9.controller
import com.fsck.k9.K9

View file

@ -7,11 +7,11 @@ import com.fsck.k9.mail.Message.RecipientType
object IdentityHelper {
private val RECIPIENT_TYPES = listOf(
RecipientType.TO,
RecipientType.CC,
RecipientType.X_ORIGINAL_TO,
RecipientType.DELIVERED_TO,
RecipientType.X_ENVELOPE_TO
RecipientType.TO,
RecipientType.CC,
RecipientType.X_ORIGINAL_TO,
RecipientType.DELIVERED_TO,
RecipientType.X_ENVELOPE_TO
)
/**
@ -30,10 +30,10 @@ object IdentityHelper {
@JvmStatic
fun getRecipientIdentityFromMessage(account: Account, message: Message): Identity {
val recipient: Identity? = RECIPIENT_TYPES.asSequence()
.flatMap { recipientType -> message.getRecipients(recipientType).asSequence() }
.map { address -> account.findIdentity(address) }
.filterNotNull()
.firstOrNull()
.flatMap { recipientType -> message.getRecipients(recipientType).asSequence() }
.map { address -> account.findIdentity(address) }
.filterNotNull()
.firstOrNull()
return recipient ?: account.getIdentity(0)
}

View file

@ -1,4 +1,5 @@
@file:JvmName("StringHelper")
package com.fsck.k9.helper
fun isNullOrEmpty(text: String?) = text.isNullOrEmpty()

View file

@ -8,8 +8,8 @@ import androidx.work.WorkerFactory
class WorkManagerProvider(private val context: Context, private val workerFactory: WorkerFactory) {
fun getWorkManager(): WorkManager {
val configuration = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
.setWorkerFactory(workerFactory)
.build()
WorkManager.initialize(context, configuration)

View file

@ -23,8 +23,8 @@ class FolderRepository(
fun getRemoteFolders(): List<RemoteFolder> {
val folders = localStoreProvider.getInstance(account).getPersonalNamespaces(false)
return folders
.filterNot { it.isLocalOnly }
.map { RemoteFolder(it.databaseId, it.serverId, it.name, it.type.toFolderType()) }
.filterNot { it.isLocalOnly }
.map { RemoteFolder(it.databaseId, it.serverId, it.name, it.type.toFolderType()) }
}
fun getDisplayFolders(displayMode: FolderMode?): List<DisplayFolder> {
@ -222,7 +222,8 @@ class FolderRepository(
}
private fun getDisplayFolders(db: SQLiteDatabase, displayMode: FolderMode): List<DisplayFolder> {
val queryBuilder = StringBuilder("""
val queryBuilder = StringBuilder(
"""
SELECT f.id, f.name, f.top_group, f.local_only, (
SELECT COUNT(m.id)
FROM messages m
@ -259,20 +260,20 @@ class FolderRepository(
FolderMode.ALL -> Unit // Return all folders
FolderMode.FIRST_CLASS -> {
query.append(" WHERE f.display_class = '")
.append(FolderClass.FIRST_CLASS.name)
.append("'")
.append(FolderClass.FIRST_CLASS.name)
.append("'")
}
FolderMode.FIRST_AND_SECOND_CLASS -> {
query.append(" WHERE f.display_class IN ('")
.append(FolderClass.FIRST_CLASS.name)
.append("', '")
.append(FolderClass.SECOND_CLASS.name)
.append("')")
.append(FolderClass.FIRST_CLASS.name)
.append("', '")
.append(FolderClass.SECOND_CLASS.name)
.append("')")
}
FolderMode.NOT_SECOND_CLASS -> {
query.append(" WHERE f.display_class != '")
.append(FolderClass.SECOND_CLASS.name)
.append("'")
.append(FolderClass.SECOND_CLASS.name)
.append("'")
}
FolderMode.NONE -> throw AssertionError("Invalid folder display mode: $displayMode")
}

View file

@ -1,4 +1,5 @@
@file:JvmName("FolderTypeConverter")
package com.fsck.k9.mailstore
import com.fsck.k9.mail.FolderType

View file

@ -29,16 +29,16 @@ class K9BackendFolder(
data class Init(val databaseId: String, val name: String, val visibleLimit: Int)
val init = database.query(
"folders",
arrayOf("id", "name", "visible_limit"),
"server_id = ?",
folderServerId
"folders",
arrayOf("id", "name", "visible_limit"),
"server_id = ?",
folderServerId
) { cursor ->
if (cursor.moveToFirst()) {
Init(
databaseId = cursor.getString(0),
name = cursor.getString(1),
visibleLimit = cursor.getInt(2)
databaseId = cursor.getString(0),
name = cursor.getString(1),
visibleLimit = cursor.getInt(2)
)
} else {
throw IllegalStateException("Couldn't find folder $folderServerId")
@ -61,9 +61,12 @@ class K9BackendFolder(
}
override fun getMessageServerIds(): Set<String> {
return database.rawQuery("SELECT uid FROM messages" +
" WHERE empty = 0 AND deleted = 0 AND folder_id = ? AND uid NOT LIKE '${K9.LOCAL_UID_PREFIX}%'" +
" ORDER BY date DESC", databaseId) { cursor ->
return database.rawQuery(
"SELECT uid FROM messages" +
" WHERE empty = 0 AND deleted = 0 AND folder_id = ? AND uid NOT LIKE '${K9.LOCAL_UID_PREFIX}%'" +
" ORDER BY date DESC",
databaseId
) { cursor ->
val result = mutableSetOf<String>()
while (cursor.moveToNext()) {
val uid = cursor.getString(0)
@ -74,9 +77,12 @@ class K9BackendFolder(
}
override fun getAllMessagesAndEffectiveDates(): Map<String, Long?> {
return database.rawQuery("SELECT uid, date FROM messages" +
return database.rawQuery(
"SELECT uid, date FROM messages" +
" WHERE empty = 0 AND deleted = 0 AND folder_id = ? AND uid NOT LIKE '${K9.LOCAL_UID_PREFIX}%'" +
" ORDER BY date DESC", databaseId) { cursor ->
" ORDER BY date DESC",
databaseId
) { cursor ->
val result = mutableMapOf<String, Long?>()
while (cursor.moveToNext()) {
val uid = cursor.getString(0)
@ -142,11 +148,12 @@ class K9BackendFolder(
return database.execute(false) { db ->
val cursor = db.query(
"messages",
arrayOf("deleted", "read", "flagged", "answered", "forwarded", "flags"),
"folder_id = ? AND uid = ?",
arrayOf(databaseId, messageServerId),
null, null, null)
"messages",
arrayOf("deleted", "read", "flagged", "answered", "forwarded", "flags"),
"folder_id = ? AND uid = ?",
arrayOf(databaseId, messageServerId),
null, null, null
)
cursor.use {
if (!cursor.moveToFirst()) {
@ -182,10 +189,10 @@ class K9BackendFolder(
Flag.FORWARDED -> database.setMessagesBoolean(messageServerId, "forwarded", value)
else -> {
val flagsColumnValue = database.getString(
table = "messages",
column = "flags",
selection = "folder_id = ? AND uid = ?",
selectionArgs = *arrayOf(databaseId, messageServerId)
table = "messages",
column = "flags",
selection = "folder_id = ? AND uid = ?",
selectionArgs = *arrayOf(databaseId, messageServerId)
) ?: ""
val flags = flagsColumnValue.split(',').toMutableSet()
@ -198,11 +205,11 @@ class K9BackendFolder(
val serializedFlags = flags.joinToString(separator = ",")
database.setString(
table = "messages",
column = "flags",
selection = "folder_id = ? AND uid = ?",
selectionArgs = *arrayOf(databaseId, messageServerId),
value = serializedFlags
table = "messages",
column = "flags",
selection = "folder_id = ? AND uid = ?",
selectionArgs = *arrayOf(databaseId, messageServerId),
value = serializedFlags
)
}
}

View file

@ -42,11 +42,12 @@ class K9BackendStorage(
override fun getExtraString(name: String): String? {
return database.execute(false) { db ->
val cursor = db.query(
"account_extra_values",
arrayOf("value_text"),
"name = ?",
arrayOf(name),
null, null, null)
"account_extra_values",
arrayOf("value_text"),
"name = ?",
arrayOf(name),
null, null, null
)
cursor.use {
if (it.moveToFirst()) {
it.getStringOrNull(0)
@ -70,11 +71,12 @@ class K9BackendStorage(
override fun getExtraNumber(name: String): Long? {
return database.execute(false) { db ->
val cursor = db.query(
"account_extra_values",
arrayOf("value_integer"),
"name = ?",
arrayOf(name),
null, null, null)
"account_extra_values",
arrayOf("value_integer"),
"name = ?",
arrayOf(name),
null, null, null
)
cursor.use {
if (it.moveToFirst()) {
it.getLongOrNull(0)

View file

@ -8,10 +8,10 @@ class OutboxStateRepository(private val database: LockableDatabase, private val
fun getOutboxState(messageId: Long): OutboxState {
return database.execute(false) { db ->
db.query(
TABLE_NAME,
COLUMNS,
"$COLUMN_MESSAGE_ID = ?",
arrayOf(messageId.toString()), null, null, null
TABLE_NAME,
COLUMNS,
"$COLUMN_MESSAGE_ID = ?",
arrayOf(messageId.toString()), null, null, null
).use { cursor ->
if (!cursor.moveToFirst()) {
throw IllegalStateException("No outbox_state entry for message with id $messageId")
@ -49,20 +49,22 @@ class OutboxStateRepository(private val database: LockableDatabase, private val
fun incrementSendAttempts(messageId: Long) {
database.execute(false) { db ->
db.execSQL("UPDATE $TABLE_NAME " +
db.execSQL(
"UPDATE $TABLE_NAME " +
"SET $COLUMN_NUMBER_OF_SEND_ATTEMPTS = $COLUMN_NUMBER_OF_SEND_ATTEMPTS + 1 " +
"WHERE $COLUMN_MESSAGE_ID = ?",
arrayOf(messageId.toString())
arrayOf(messageId.toString())
)
}
}
fun decrementSendAttempts(messageId: Long) {
database.execute(false) { db ->
db.execSQL("UPDATE $TABLE_NAME " +
db.execSQL(
"UPDATE $TABLE_NAME " +
"SET $COLUMN_NUMBER_OF_SEND_ATTEMPTS = $COLUMN_NUMBER_OF_SEND_ATTEMPTS - 1 " +
"WHERE $COLUMN_MESSAGE_ID = ?",
arrayOf(messageId.toString())
arrayOf(messageId.toString())
)
}
}
@ -104,10 +106,10 @@ class OutboxStateRepository(private val database: LockableDatabase, private val
private const val COLUMN_ERROR = "error"
private val COLUMNS = arrayOf(
COLUMN_SEND_STATE,
COLUMN_NUMBER_OF_SEND_ATTEMPTS,
COLUMN_ERROR_TIMESTAMP,
COLUMN_ERROR
COLUMN_SEND_STATE,
COLUMN_NUMBER_OF_SEND_ATTEMPTS,
COLUMN_ERROR_TIMESTAMP,
COLUMN_ERROR
)
}
}

View file

@ -9,7 +9,7 @@ enum class SendState(val databaseName: String) {
@JvmStatic
fun fromDatabaseName(databaseName: String): SendState {
return SendState.values().firstOrNull { it.databaseName == databaseName }
?: throw IllegalArgumentException("Unknown value: $databaseName")
?: throw IllegalArgumentException("Unknown value: $databaseName")
}
}
}

View file

@ -6,8 +6,10 @@ import org.jsoup.safety.Whitelist as AllowList
internal class BodyCleaner {
private val cleaner: Cleaner
private val allowedBodyAttributes = setOf("id", "class", "dir", "lang", "style",
"alink", "background", "bgcolor", "link", "text", "vlink")
private val allowedBodyAttributes = setOf(
"id", "class", "dir", "lang", "style",
"alink", "background", "bgcolor", "link", "text", "vlink"
)
init {
val allowList = AllowList.relaxed()

View file

@ -9,19 +9,19 @@ class DisplayHtml(private val settings: HtmlSettings) {
fun wrapMessageContent(messageContent: CharSequence): String {
// Include a meta tag so the WebView will not use a fixed viewport width of 980 px
return "<html dir=\"auto\"><head><meta name=\"viewport\" content=\"width=device-width\"/>" +
cssStyleTheme() +
cssStylePre() +
"</head><body>" +
messageContent +
"</body></html>"
cssStyleTheme() +
cssStylePre() +
"</head><body>" +
messageContent +
"</body></html>"
}
fun cssStyleTheme(): String {
return if (settings.useDarkMode) {
"<style type=\"text/css\">" +
"* { background: black ! important; color: #F3F3F3 !important }" +
":link, :link * { color: #CCFF33 !important }" +
":visited, :visited * { color: #551A8B !important }</style> "
"* { background: black ! important; color: #F3F3F3 !important }" +
":link, :link * { color: #CCFF33 !important }" +
":visited, :visited * { color: #551A8B !important }</style> "
} else {
""
}
@ -39,8 +39,8 @@ class DisplayHtml(private val settings: HtmlSettings) {
val font = if (settings.useFixedWidthFont) "monospace" else "sans-serif"
return "<style type=\"text/css\"> pre." + EmailTextToHtml.K9MAIL_CSS_CLASS +
" {white-space: pre-wrap; word-wrap:break-word; " +
"font-family: " + font + "; margin-top: 0px}</style>"
" {white-space: pre-wrap; word-wrap:break-word; " +
"font-family: " + font + "; margin-top: 0px}</style>"
}
fun cssStyleSignature(): String {

View file

@ -3,13 +3,15 @@ package com.fsck.k9.message.html
internal object DividerReplacer : TextToHtml.HtmlModifier {
private const val SIMPLE_DIVIDER = "[-=_]{3,}"
private const val ASCII_SCISSORS = "(?:-{2,}\\s?(?:>[%8]|[%8]<)\\s?-{2,})+"
private val PATTERN = Regex("(?:^|\\n)" +
private val PATTERN = Regex(
"(?:^|\\n)" +
"(?:" +
"\\s*" +
"(?:" + SIMPLE_DIVIDER + "|" + ASCII_SCISSORS + ")" +
"\\s*" +
"(?:\\n|$)" +
")+")
"\\s*" +
"(?:" + SIMPLE_DIVIDER + "|" + ASCII_SCISSORS + ")" +
"\\s*" +
"(?:\\n|$)" +
")+"
)
override fun findModifications(text: CharSequence): List<HtmlModification> {
return PATTERN.findAll(text).map { matchResult ->

View file

@ -36,9 +36,11 @@ class EmailTextToHtml private constructor(private val text: String) {
}
} else if (quoteDepth > previousQuoteDepth) {
for (depth in (previousQuoteDepth + 1)..quoteDepth) {
html.append("<blockquote " +
html.append(
"<blockquote " +
"class=\"gmail_quote\" " +
"style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid ")
"style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid "
)
html.append(quoteColor(depth))
html.append("; padding-left: 1ex;\">")
}

View file

@ -28,8 +28,8 @@ object HtmlConverter {
fun htmlToText(html: String): String {
val document = Jsoup.parse(html)
return HtmlToPlainText.toPlainText(document.body())
.replace(PREVIEW_OBJECT_CHARACTER, PREVIEW_OBJECT_REPLACEMENT)
.replace(NBSP_CHARACTER, NBSP_REPLACEMENT)
.replace(PREVIEW_OBJECT_CHARACTER, PREVIEW_OBJECT_REPLACEMENT)
.replace(NBSP_CHARACTER, NBSP_REPLACEMENT)
}
/**

View file

@ -178,7 +178,8 @@ internal class HttpUriParser : UriParser {
// 1) No compression and full address, everything fine
// 2) Compression enabled and whole address parsed, everything fine as well
if (!compressionEnabled && beginSegmentsCount + endSegmentsCount == 8 ||
compressionEnabled && beginSegmentsCount + endSegmentsCount < 8) {
compressionEnabled && beginSegmentsCount + endSegmentsCount < 8
) {
// Only optional port left, skip address bracket
currentPos++
} else {

View file

@ -5,8 +5,8 @@ import java.util.ArrayDeque
class TextToHtml private constructor(private val text: CharSequence, private val html: StringBuilder) {
fun appendAsHtmlFragment() {
val modifications = HTML_MODIFIERS
.flatMap { it.findModifications(text) }
.sortedBy { it.startIndex }
.flatMap { it.findModifications(text) }
.sortedBy { it.startIndex }
val modificationStack = ArrayDeque<HtmlModification.Wrap>()
var currentIndex = 0
@ -21,8 +21,10 @@ class TextToHtml private constructor(private val text: CharSequence, private val
appendHtmlEncoded(currentIndex, modification.startIndex)
if (modification.endIndex > modificationStack.peek()?.endIndex ?: Int.MAX_VALUE) {
error("HtmlModification $modification must be fully contained within " +
"outer HtmlModification ${modificationStack.peek()}")
error(
"HtmlModification $modification must be fully contained within " +
"outer HtmlModification ${modificationStack.peek()}"
)
}
when (modification) {

View file

@ -5,19 +5,19 @@ import java.util.Locale
object UriMatcher {
private val SUPPORTED_URIS = { httpUriParser: HttpUriParser ->
mapOf(
"ethereum:" to EthereumUriParser(),
"bitcoin:" to BitcoinUriParser(),
"http:" to httpUriParser,
"https:" to httpUriParser,
"rtsp:" to httpUriParser
"ethereum:" to EthereumUriParser(),
"bitcoin:" to BitcoinUriParser(),
"http:" to httpUriParser,
"https:" to httpUriParser,
"rtsp:" to httpUriParser
)
}.invoke(HttpUriParser())
private const val SCHEME_SEPARATORS = "\\s(\\n<"
private const val ALLOWED_SEPARATORS_PATTERN = "(?:^|[$SCHEME_SEPARATORS])"
private val URI_SCHEME = Regex(
"$ALLOWED_SEPARATORS_PATTERN(${ SUPPORTED_URIS.keys.joinToString("|") })",
RegexOption.IGNORE_CASE
"$ALLOWED_SEPARATORS_PATTERN(${ SUPPORTED_URIS.keys.joinToString("|") })",
RegexOption.IGNORE_CASE
)
fun findUris(text: CharSequence): List<UriMatch> {

View file

@ -13,10 +13,10 @@ val coreNotificationModule = module {
single { NotificationHelper(get(), get(), get()) }
single {
NotificationChannelManager(
get(),
Executors.newSingleThreadExecutor(),
get<Context>().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager,
get()
get(),
Executors.newSingleThreadExecutor(),
get<Context>().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager,
get()
)
}
single { AccountPreferenceSerializer(get(), get()) }

View file

@ -68,8 +68,10 @@ class NotificationHelper(
account: Account,
channelType: NotificationChannelManager.ChannelType
): NotificationCompat.Builder {
return NotificationCompat.Builder(context,
channelUtils.getChannelIdFor(account, channelType))
return NotificationCompat.Builder(
context,
channelUtils.getChannelIdFor(account, channelType)
)
}
companion object {

View file

@ -90,8 +90,10 @@ class SettingsExporter(
try {
writeKeyAndPrettyValueFromSetting(serializer, key, setting, valueString)
} catch (e: InvalidSettingValueException) {
Timber.w("Global setting \"%s\" has invalid value \"%s\" in preference storage. " +
"This shouldn't happen!", key, valueString)
Timber.w(
"Global setting \"%s\" has invalid value \"%s\" in preference storage. This shouldn't happen!",
key, valueString
)
}
} else {
Timber.d("Couldn't find key \"%s\" in preference storage. Using default value.", key)
@ -264,7 +266,8 @@ class SettingsExporter(
} catch (e: InvalidSettingValueException) {
Timber.w(
"Account setting \"%s\" (%s) has invalid value \"%s\" in preference storage. " +
"This shouldn't happen!", keyPart, account.description, valueString
"This shouldn't happen!",
keyPart, account.description, valueString
)
}
}
@ -353,8 +356,10 @@ class SettingsExporter(
try {
writeKeyAndPrettyValueFromSetting(serializer, identityKey, setting, valueString)
} catch (e: InvalidSettingValueException) {
Timber.w("Identity setting \"%s\" has invalid value \"%s\" in preference storage. " +
"This shouldn't happen!", identityKey, valueString
Timber.w(
"Identity setting \"%s\" has invalid value \"%s\" in preference storage. " +
"This shouldn't happen!",
identityKey, valueString
)
}
}
@ -390,8 +395,10 @@ class SettingsExporter(
try {
writeKeyAndPrettyValueFromSetting(serializer, key, setting, value)
} catch (e: InvalidSettingValueException) {
Timber.w("Folder setting \"%s\" has invalid value \"%s\" in preference storage. " +
"This shouldn't happen!", key, value)
Timber.w(
"Folder setting \"%s\" has invalid value \"%s\" in preference storage. This shouldn't happen!",
key, value
)
}
}
}

View file

@ -1,4 +1,5 @@
@file:JvmName("LocalSearchExtensions")
package com.fsck.k9.search
import com.fsck.k9.Account

View file

@ -75,7 +75,8 @@ class AutocryptGossipHeaderParserTest {
@Test
fun parseHeader_missingKeydata() {
val gossipHeader = autocryptGossipHeaderParser.parseAutocryptGossipHeader(
"addr=CDEF")
"addr=CDEF"
)
assertNull(gossipHeader)
}
@ -83,7 +84,8 @@ class AutocryptGossipHeaderParserTest {
@Test
fun parseHeader_unknownCritical() {
val gossipHeader = autocryptGossipHeaderParser.parseAutocryptGossipHeader(
"addr=bawb; somecritical=value; keydata=aGk")
"addr=bawb; somecritical=value; keydata=aGk"
)
assertNull(gossipHeader)
}
@ -91,7 +93,8 @@ class AutocryptGossipHeaderParserTest {
@Test
fun parseHeader_unknownNonCritical() {
val gossipHeader = autocryptGossipHeaderParser.parseAutocryptGossipHeader(
"addr=bawb; _somenoncritical=value; keydata=aGk")
"addr=bawb; _somenoncritical=value; keydata=aGk"
)
assertNotNull(gossipHeader)
}
@ -99,7 +102,8 @@ class AutocryptGossipHeaderParserTest {
@Test
fun parseHeader_brokenBase64() {
val gossipHeader = autocryptGossipHeaderParser.parseAutocryptGossipHeader(
"addr=bawb; _somenoncritical=value; keydata=X")
"addr=bawb; _somenoncritical=value; keydata=X"
)
assertNull(gossipHeader)
}

View file

@ -9,8 +9,8 @@ class OpenPgpApiHelperTest {
@Test
fun buildUserId_withName_shouldCreateOpenPgpAccountName() {
val identity = Identity(
email = "user@domain.com",
name = "Name"
email = "user@domain.com",
name = "Name"
)
val result = OpenPgpApiHelper.buildUserId(identity)
@ -21,7 +21,7 @@ class OpenPgpApiHelperTest {
@Test
fun buildUserId_withoutName_shouldCreateOpenPgpAccountName() {
val identity = Identity(
email = "user@domain.com"
email = "user@domain.com"
)
val result = OpenPgpApiHelper.buildUserId(identity)

View file

@ -18,11 +18,12 @@ class IdentityHelperTest : RobolectricTest() {
@Test
fun getRecipientIdentityFromMessage_prefersToOverCc() {
val message = messageWithRecipients(
RecipientType.TO to IDENTITY_1_ADDRESS,
RecipientType.CC to IDENTITY_2_ADDRESS,
RecipientType.X_ORIGINAL_TO to IDENTITY_3_ADDRESS,
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS)
RecipientType.TO to IDENTITY_1_ADDRESS,
RecipientType.CC to IDENTITY_2_ADDRESS,
RecipientType.X_ORIGINAL_TO to IDENTITY_3_ADDRESS,
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS
)
val identity = IdentityHelper.getRecipientIdentityFromMessage(account, message)
@ -32,11 +33,12 @@ class IdentityHelperTest : RobolectricTest() {
@Test
fun getRecipientIdentityFromMessage_prefersCcOverXOriginalTo() {
val message = messageWithRecipients(
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to IDENTITY_2_ADDRESS,
RecipientType.X_ORIGINAL_TO to IDENTITY_3_ADDRESS,
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS)
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to IDENTITY_2_ADDRESS,
RecipientType.X_ORIGINAL_TO to IDENTITY_3_ADDRESS,
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS
)
val identity = IdentityHelper.getRecipientIdentityFromMessage(account, message)
@ -46,11 +48,12 @@ class IdentityHelperTest : RobolectricTest() {
@Test
fun getRecipientIdentityFromMessage_prefersXOriginalToOverDeliveredTo() {
val message = messageWithRecipients(
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to IDENTITY_3_ADDRESS,
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS)
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to IDENTITY_3_ADDRESS,
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS
)
val identity = IdentityHelper.getRecipientIdentityFromMessage(account, message)
@ -60,11 +63,12 @@ class IdentityHelperTest : RobolectricTest() {
@Test
fun getRecipientIdentityFromMessage_prefersDeliveredToOverXEnvelopeTo() {
val message = messageWithRecipients(
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to "unrelated3@example.org",
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS)
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to "unrelated3@example.org",
RecipientType.DELIVERED_TO to IDENTITY_4_ADDRESS,
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS
)
val identity = IdentityHelper.getRecipientIdentityFromMessage(account, message)
@ -74,11 +78,12 @@ class IdentityHelperTest : RobolectricTest() {
@Test
fun getRecipientIdentityFromMessage_usesXEnvelopeToWhenPresent() {
val message = messageWithRecipients(
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to "unrelated3@example.org",
RecipientType.DELIVERED_TO to "unrelated4@example.org",
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS)
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to "unrelated3@example.org",
RecipientType.DELIVERED_TO to "unrelated4@example.org",
RecipientType.X_ENVELOPE_TO to IDENTITY_5_ADDRESS
)
val identity = IdentityHelper.getRecipientIdentityFromMessage(account, message)
@ -88,11 +93,12 @@ class IdentityHelperTest : RobolectricTest() {
@Test
fun getRecipientIdentityFromMessage_withoutAnyIdentityAddresses_returnsFirstIdentity() {
val message = messageWithRecipients(
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to "unrelated3@example.org",
RecipientType.DELIVERED_TO to "unrelated4@example.org",
RecipientType.X_ENVELOPE_TO to "unrelated5@example.org")
RecipientType.TO to "unrelated1@example.org",
RecipientType.CC to "unrelated2@example.org",
RecipientType.X_ORIGINAL_TO to "unrelated3@example.org",
RecipientType.DELIVERED_TO to "unrelated4@example.org",
RecipientType.X_ENVELOPE_TO to "unrelated5@example.org"
)
val identity = IdentityHelper.getRecipientIdentityFromMessage(account, message)
@ -110,18 +116,18 @@ class IdentityHelperTest : RobolectricTest() {
private fun createDummyAccount() = Account(UUID.randomUUID().toString()).apply {
identities = listOf(
newIdentity("Default", DEFAULT_ADDRESS),
newIdentity("Identity 1", IDENTITY_1_ADDRESS),
newIdentity("Identity 2", IDENTITY_2_ADDRESS),
newIdentity("Identity 3", IDENTITY_3_ADDRESS),
newIdentity("Identity 4", IDENTITY_4_ADDRESS),
newIdentity("Identity 5", IDENTITY_5_ADDRESS)
newIdentity("Default", DEFAULT_ADDRESS),
newIdentity("Identity 1", IDENTITY_1_ADDRESS),
newIdentity("Identity 2", IDENTITY_2_ADDRESS),
newIdentity("Identity 3", IDENTITY_3_ADDRESS),
newIdentity("Identity 4", IDENTITY_4_ADDRESS),
newIdentity("Identity 5", IDENTITY_5_ADDRESS)
)
}
private fun newIdentity(name: String, email: String) = Identity(
name = name,
email = email
name = name,
email = email
)
private fun messageWithRecipients(vararg recipients: Pair<RecipientType, String>): Message {

View file

@ -17,7 +17,6 @@ import com.fsck.k9.mail.internet.MimeMessage
import com.fsck.k9.mail.internet.MimeMessageHelper
import com.fsck.k9.mail.internet.TextBody
import com.fsck.k9.provider.EmailProvider
import java.lang.IllegalStateException
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@ -151,10 +150,10 @@ class K9BackendFolderTest : K9RobolectricTest() {
private fun setFlagsColumnToNull() {
dbOperation { db ->
val numberOfUpdatedRows = db.update(
"messages",
contentValuesOf("flags" to null),
"uid = ?",
arrayOf(MESSAGE_SERVER_ID)
"messages",
contentValuesOf("flags" to null),
"uid = ?",
arrayOf(MESSAGE_SERVER_ID)
)
assertEquals(1, numberOfUpdatedRows)
}

View file

@ -58,7 +58,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_shouldStripSignature() {
val text = """
val text =
"""
Some text
--
Signature
@ -72,7 +73,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_shouldStripHorizontalLine() {
val text = """
val text =
"""
line 1
----
line 2
@ -86,7 +88,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_shouldStripQuoteHeaderAndQuotedText() {
val text = """
val text =
"""
some text
On 01/02/03 someone wrote:
@ -102,7 +105,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_shouldStripGenericQuoteHeader() {
val text = """
val text =
"""
Am 13.12.2015 um 23:42 schrieb Hans:
> hallo
hi there
@ -117,7 +121,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_shouldStripHorizontalRules() {
val text = """
val text =
"""
line 1------------------------------
line 2
""".trimIndent()
@ -150,7 +155,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_lineEndingWithColon() {
val text = """
val text =
"""
Here's a list:
- item 1
- item 2
@ -164,7 +170,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_inlineReplies() {
val text = """
val text =
"""
On 2020-09-30 at 03:12 Bob wrote:
> Hi Alice
Hi Bob
@ -184,7 +191,8 @@ class PreviewTextExtractorTest {
@Test
fun extractPreview_quoteHeaderContainingLineBreak() {
val text = """
val text =
"""
Reply text
On 2020-09-30 at 03:12

View file

@ -49,7 +49,9 @@ class DisplayHtmlTest {
private fun assertHtmlContainsElement(html: String, cssQuery: String, numberOfExpectedOccurrences: Int = 1) {
val document = Jsoup.parse(html)
val numberOfFoundElements = document.select(cssQuery).size
assertEquals("Expected to find '$cssQuery' $numberOfExpectedOccurrences time(s) in:\n$html",
numberOfExpectedOccurrences, numberOfFoundElements)
assertEquals(
"Expected to find '$cssQuery' $numberOfExpectedOccurrences time(s) in:\n$html",
numberOfExpectedOccurrences, numberOfFoundElements
)
}
}

View file

@ -6,7 +6,8 @@ import org.junit.Test
class EmailSectionExtractorTest {
@Test
fun simpleMessageWithoutQuotes() {
val message = """
val message =
"""
Hi Alice,
are we still on for new Thursday?
@ -39,7 +40,8 @@ class EmailSectionExtractorTest {
@Test
fun quoteFollowedByReply() {
val message = """
val message =
"""
Alice <alice@example.org> wrote:
> Hi there
@ -65,7 +67,8 @@ class EmailSectionExtractorTest {
@Test
fun replyFollowedByTwoQuoteLevels() {
val message = """
val message =
"""
Three
Bob <bob@example.org> wrote:
@ -94,9 +97,11 @@ class EmailSectionExtractorTest {
@Test
fun quoteEndingWithEmptyLineButNoNewline() {
val message = """
val message =
"""
> Quoted text
> """.trimIndent()
>
""".trimIndent()
val sections = EmailSectionExtractor.extract(message)
@ -112,7 +117,8 @@ class EmailSectionExtractorTest {
@Test
fun chaosQuoting() {
val message = """
val message =
"""
>>> One
> Three
Four
@ -142,7 +148,8 @@ class EmailSectionExtractorTest {
@Test
fun quotedSectionStartingWithEmptyLine() {
val message = """
val message =
"""
Quote header:
>
> Quoted text
@ -163,7 +170,7 @@ class EmailSectionExtractorTest {
@Test
fun quotedBlankLinesShouldNotContributeToIndentValue() {
val message =
val message = "" +
">\n" +
"> Quoted text\n" +
"> \n" +

View file

@ -9,7 +9,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshInHead() {
val html = """
val html =
"""
<html>
<head><meta http-equiv="refresh" content="1; URL=http://example.com/"></head>
<body>Message</body>
@ -23,7 +24,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshBetweenHeadAndBody() {
val html = """
val html =
"""
<html>
<head></head>
<meta http-equiv="refresh" content="1; URL=http://example.com/">
@ -38,11 +40,13 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshInBody() {
val html = """
val html =
"""
<html>
<head></head>
<body><meta http-equiv="refresh" content="1; URL=http://example.com/">Message</body>
</html>""".trimIndent().trimLineBreaks()
</html>
""".trimIndent().trimLineBreaks()
val result = htmlSanitizer.sanitize(html)
@ -51,7 +55,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshWithUpperCaseAttributeValue() {
val html = """
val html =
"""
<html>
<head><meta http-equiv="REFRESH" content="1; URL=http://example.com/"></head>
<body>Message</body>
@ -65,7 +70,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshWithMixedCaseAttributeValue() {
val html = """
val html =
"""
<html>
<head><meta http-equiv="Refresh" content="1; URL=http://example.com/"></head>
<body>Message</body>
@ -79,7 +85,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshWithoutQuotesAroundAttributeValue() {
val html = """
val html =
"""
<html>
<head><meta http-equiv=refresh content="1; URL=http://example.com/"></head>
<body>Message</body>
@ -93,7 +100,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshWithSpacesInAttributeValue() {
val html = """
val html =
"""
<html>
<head><meta http-equiv="refresh " content="1; URL=http://example.com/"></head>
<body>Message</body>
@ -107,7 +115,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMultipleMetaRefreshTags() {
val html = """
val html =
"""
<html>
<head><meta http-equiv="refresh" content="1; URL=http://example.com/"></head>
<body><meta http-equiv="refresh" content="1; URL=http://example.com/">Message</body>
@ -121,7 +130,8 @@ class HtmlSanitizerTest {
@Test
fun shouldRemoveMetaRefreshButKeepOtherMetaTags() {
val html = """
val html =
"""
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
@ -145,7 +155,8 @@ class HtmlSanitizerTest {
@Test
fun shouldProduceValidHtmlFromHtmlWithXmlDeclaration() {
val html = """
val html =
"""
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head></head>
@ -171,7 +182,8 @@ class HtmlSanitizerTest {
@Test
fun shouldHtmlEncodeXmlDirectives() {
val html = """
val html =
"""
<html>
<head></head>
<body>
@ -212,7 +224,8 @@ class HtmlSanitizerTest {
@Test
fun shouldKeepMapAreaTags() {
val html = """
val html =
"""
<html>
<head></head>
<body>
@ -232,7 +245,8 @@ class HtmlSanitizerTest {
@Test
fun shouldKeepImgUsemap() {
val html = """
val html =
"""
<html>
<head></head>
<body><img src="http://domain.com/image.jpg" usemap="#planetmap"></body>
@ -246,7 +260,8 @@ class HtmlSanitizerTest {
@Test
fun shouldKeepAllowedElementsInHeadAndSkipTheRest() {
val html = """
val html =
"""
<html>
<head>
<title>remove this</title>
@ -288,7 +303,8 @@ class HtmlSanitizerTest {
@Test
fun shouldKeepUris() {
val html = """
val html =
"""
<html>
<body>
<a href="http://example.com/index.html">HTTP</a>
@ -326,7 +342,8 @@ class HtmlSanitizerTest {
@Test
fun shouldKeepDirAttribute() {
val html = """
val html =
"""
<html>
<head></head>
<body><table><tbody><tr><td dir="rtl"></td></tr></tbody></table></body>
@ -340,7 +357,8 @@ class HtmlSanitizerTest {
@Test
fun shouldKeepAllowedBodyAttributes() {
val html = """
val html =
"""
<html>
<body style="color: #fff" onload="alert()" class="body" id></body>
</html>

View file

@ -36,12 +36,14 @@ class TextQuoteCreatorTest : RobolectricTest() {
val quote = createQuote(messageBody, quoteStyle, quotePrefix)
assertThat(quote).isEqualTo("""
assertThat(quote).isEqualTo(
"""
On January 18, 1970 7:53:41 PM UTC, Alice <alice@sender.example> wrote:
> Line 1
> Line 2
> Line 3
""".trimIndent().crlf())
""".trimIndent().crlf()
)
}
@Test
@ -52,16 +54,19 @@ class TextQuoteCreatorTest : RobolectricTest() {
val quote = createQuote(messageBody, quoteStyle, quotePrefix)
assertThat(quote).isEqualTo("""
assertThat(quote).isEqualTo(
"""
On January 18, 1970 7:53:41 PM UTC, Alice <alice@sender.example> wrote:
$1\t Line 1
$1\t Line 2
""".trimIndent().crlf())
""".trimIndent().crlf()
)
}
@Test
fun prefixQuote_withLongLines() {
val messageBody = """
val messageBody =
"""
[-------] [-------] [-------] [-------] [-------] [-------] [-------] [-------] [-------] [-------]
[-------------------------------------------------------------------------------------------------]
""".trimIndent().crlf()
@ -70,11 +75,13 @@ class TextQuoteCreatorTest : RobolectricTest() {
val quote = createQuote(messageBody, quoteStyle, quotePrefix)
assertThat(quote).isEqualTo("""
assertThat(quote).isEqualTo(
"""
On January 18, 1970 7:53:41 PM UTC, Alice <alice@sender.example> wrote:
> [-------] [-------] [-------] [-------] [-------] [-------] [-------] [-------] [-------] [-------]
> [-------------------------------------------------------------------------------------------------]
""".trimIndent().crlf())
""".trimIndent().crlf()
)
}
@Test
@ -84,7 +91,8 @@ class TextQuoteCreatorTest : RobolectricTest() {
val quote = createQuote(messageBody, quoteStyle)
assertThat(quote).isEqualTo("""
assertThat(quote).isEqualTo(
"""
-------- Original Message --------
From: Alice <alice@sender.example>
@ -95,7 +103,8 @@ class TextQuoteCreatorTest : RobolectricTest() {
Line 1
Line 2
Line 3
""".trimIndent().crlf())
""".trimIndent().crlf()
)
}
private fun createQuote(messageBody: String, quoteStyle: QuoteStyle, quotePrefix: String = ""): String {

View file

@ -22,7 +22,7 @@ class TestNotificationResourceProvider : NotificationResourceProvider {
override fun authenticationErrorTitle(): String = "Authentication failed"
override fun authenticationErrorBody(accountName: String): String =
"Authentication failed for $accountName. Update your server settings."
"Authentication failed for $accountName. Update your server settings."
override fun certificateErrorTitle(accountName: String): String = "Certificate error for $accountName"
@ -31,7 +31,7 @@ class TestNotificationResourceProvider : NotificationResourceProvider {
override fun newMailTitle(): String = "New mail"
override fun newMailUnreadMessageCount(unreadMessageCount: Int, accountName: String): String =
"$unreadMessageCount Unread ($accountName)"
"$unreadMessageCount Unread ($accountName)"
override fun newMessagesTitle(newMessagesCount: Int): String = when (newMessagesCount) {
1 -> "1 new message"
@ -39,7 +39,7 @@ class TestNotificationResourceProvider : NotificationResourceProvider {
}
override fun additionalMessages(overflowMessagesCount: Int, accountName: String): String =
"+ $overflowMessagesCount more on $accountName"
"+ $overflowMessagesCount more on $accountName"
override fun previewEncrypted(): String = "*Encrypted*"
@ -56,7 +56,7 @@ class TestNotificationResourceProvider : NotificationResourceProvider {
override fun sendingMailBody(accountName: String): String = "Sending mail: $accountName"
override fun checkingMailTicker(accountName: String, folderName: String): String =
"Checking mail: $accountName:$folderName"
"Checking mail: $accountName:$folderName"
override fun checkingMailTitle(): String = "Checking mail"

View file

@ -1,4 +1,5 @@
@file:JvmName("MessagingControllerTestExtra")
package com.fsck.k9.preferences
import com.fsck.k9.Account
@ -38,7 +39,9 @@ fun setUpBackendManager() {
}
}
loadKoinModules(module {
single(override = true) { BackendManager(mapOf("imap" to backendFactory)) }
})
loadKoinModules(
module {
single(override = true) { BackendManager(mapOf("imap" to backendFactory)) }
}
)
}

View file

@ -12,8 +12,8 @@ private const val TEST_INT_VALUE = "4"
private const val TEST_STRING_DEFAULT = "z"
private const val TEST_INT_DEFAULT = 2
private val TEST_MAP = mapOf(
TEST_STRING_KEY to TEST_STRING_VALUE,
TEST_INT_KEY to TEST_INT_VALUE
TEST_STRING_KEY to TEST_STRING_VALUE,
TEST_INT_KEY to TEST_INT_VALUE
)
class StorageTest {

View file

@ -29,7 +29,7 @@ class App : Application() {
companion object {
val appConfig = AppConfig(
componentsToDisable = listOf(MessageCompose::class.java)
componentsToDisable = listOf(MessageCompose::class.java)
)
}
}

View file

@ -22,10 +22,10 @@ private val mainAppModule = module {
}
val appModules = listOf(
mainAppModule,
notificationModule,
resourcesModule,
backendsModule,
storageModule,
uiAddAccountModule
mainAppModule,
notificationModule,
resourcesModule,
backendsModule,
storageModule,
uiAddAccountModule
)

View file

@ -41,11 +41,11 @@ class ImapBackendFactory(
val serverSettings = ImapStoreUriDecoder.decode(account.storeUri)
val config = createImapStoreConfig(account)
return ImapStore(
serverSettings,
config,
trustedSocketFactory,
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
oAuth2TokenProvider
serverSettings,
config,
trustedSocketFactory,
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
oAuth2TokenProvider
)
}

View file

@ -7,12 +7,13 @@ import org.koin.dsl.module
val backendsModule = module {
single {
BackendManager(
mapOf(
"imap" to get<ImapBackendFactory>(),
"pop3" to get<Pop3BackendFactory>(),
"webdav" to get<WebDavBackendFactory>(),
"jmap" to get<JmapBackendFactory>()
))
mapOf(
"imap" to get<ImapBackendFactory>(),
"pop3" to get<Pop3BackendFactory>(),
"webdav" to get<WebDavBackendFactory>(),
"jmap" to get<JmapBackendFactory>()
)
)
}
single { ImapBackendFactory(get(), get(), get(), get()) }
single { Pop3BackendFactory(get(), get()) }

View file

@ -27,34 +27,36 @@ class K9NotificationResourceProvider(private val context: Context) : Notificatio
get() = context.getString(R.string.notification_channel_miscellaneous_description)
override fun authenticationErrorTitle(): String =
context.getString(R.string.notification_authentication_error_title)
context.getString(R.string.notification_authentication_error_title)
override fun authenticationErrorBody(accountName: String): String =
context.getString(R.string.notification_authentication_error_text, accountName)
context.getString(R.string.notification_authentication_error_text, accountName)
override fun certificateErrorTitle(accountName: String): String =
context.getString(R.string.notification_certificate_error_title, accountName)
context.getString(R.string.notification_certificate_error_title, accountName)
override fun certificateErrorBody(): String = context.getString(R.string.notification_certificate_error_text)
override fun newMailTitle(): String = context.getString(R.string.notification_new_title)
override fun newMailUnreadMessageCount(unreadMessageCount: Int, accountName: String): String =
context.getString(R.string.notification_new_one_account_fmt, unreadMessageCount, accountName)
context.getString(R.string.notification_new_one_account_fmt, unreadMessageCount, accountName)
override fun newMessagesTitle(newMessagesCount: Int): String =
context.resources.getQuantityString(R.plurals.notification_new_messages_title,
newMessagesCount, newMessagesCount)
context.resources.getQuantityString(
R.plurals.notification_new_messages_title,
newMessagesCount, newMessagesCount
)
override fun additionalMessages(overflowMessagesCount: Int, accountName: String): String =
context.getString(R.string.notification_additional_messages, overflowMessagesCount, accountName)
context.getString(R.string.notification_additional_messages, overflowMessagesCount, accountName)
override fun previewEncrypted(): String = context.getString(R.string.preview_encrypted)
override fun noSubject(): String = context.getString(R.string.general_no_subject)
override fun recipientDisplayName(recipientDisplayName: String): String =
context.getString(R.string.message_to_fmt, recipientDisplayName)
context.getString(R.string.message_to_fmt, recipientDisplayName)
override fun noSender(): String = context.getString(R.string.general_no_sender)
@ -63,16 +65,16 @@ class K9NotificationResourceProvider(private val context: Context) : Notificatio
override fun sendingMailTitle(): String = context.getString(R.string.notification_bg_send_title)
override fun sendingMailBody(accountName: String): String =
context.getString(R.string.notification_bg_send_ticker, accountName)
context.getString(R.string.notification_bg_send_ticker, accountName)
override fun checkingMailTicker(accountName: String, folderName: String): String =
context.getString(R.string.notification_bg_sync_ticker, accountName, folderName)
context.getString(R.string.notification_bg_sync_ticker, accountName, folderName)
override fun checkingMailTitle(): String =
context.getString(R.string.notification_bg_sync_title)
context.getString(R.string.notification_bg_sync_title)
override fun checkingMailSeparator(): String =
context.getString(R.string.notification_bg_title_separator)
context.getString(R.string.notification_bg_title_separator)
override fun actionMarkAsRead(): String = context.getString(R.string.notification_action_mark_as_read)

View file

@ -9,10 +9,10 @@ class K9CoreResourceProvider(private val context: Context) : CoreResourceProvide
override fun defaultIdentityDescription(): String = context.getString(R.string.default_identity_description)
override fun internalStorageProviderName(): String =
context.getString(R.string.local_storage_provider_internal_label)
context.getString(R.string.local_storage_provider_internal_label)
override fun externalStorageProviderName(): String =
context.getString(R.string.local_storage_provider_external_label)
context.getString(R.string.local_storage_provider_external_label)
override fun contactDisplayNamePrefix(): String = context.getString(R.string.message_to_label)
override fun contactUnknownSender(): String = context.getString(R.string.unknown_sender)
@ -31,10 +31,10 @@ class K9CoreResourceProvider(private val context: Context) : CoreResourceProvide
override fun encryptedSubject(): String = context.getString(R.string.encrypted_subject)
override fun replyHeader(sender: String): String =
context.getString(R.string.message_compose_reply_header_fmt, sender)
context.getString(R.string.message_compose_reply_header_fmt, sender)
override fun replyHeader(sender: String, sentDate: String): String =
context.getString(R.string.message_compose_reply_header_fmt_with_date, sentDate, sender)
context.getString(R.string.message_compose_reply_header_fmt_with_date, sentDate, sender)
override fun searchAllMessagesTitle(): String = context.getString(R.string.search_all_messages_title)
override fun searchAllMessagesDetail(): String = context.getString(R.string.search_all_messages_detail)

View file

@ -31,7 +31,7 @@ class App : Application() {
companion object {
val appConfig = AppConfig(
componentsToDisable = listOf(MessageCompose::class.java)
componentsToDisable = listOf(MessageCompose::class.java)
)
}
}

View file

@ -18,11 +18,13 @@ import org.koin.dsl.module
private val mainAppModule = module {
single { App.appConfig }
single { MessagingListenerProvider(
single {
MessagingListenerProvider(
listOf(
get<UnreadWidgetUpdateListener>(),
get<MessageListWidgetUpdateListener>()
))
get<UnreadWidgetUpdateListener>(),
get<MessageListWidgetUpdateListener>()
)
)
}
single(named("controllerExtensions")) { emptyList<ControllerExtension>() }
single<EncryptionExtractor> { OpenPgpEncryptionExtractor.newInstance() }
@ -30,11 +32,11 @@ private val mainAppModule = module {
}
val appModules = listOf(
mainAppModule,
messageListWidgetModule,
unreadWidgetModule,
notificationModule,
resourcesModule,
backendsModule,
storageModule
mainAppModule,
messageListWidgetModule,
unreadWidgetModule,
notificationModule,
resourcesModule,
backendsModule,
storageModule
)

View file

@ -41,11 +41,11 @@ class ImapBackendFactory(
val serverSettings = ImapStoreUriDecoder.decode(account.storeUri)
val config = createImapStoreConfig(account)
return ImapStore(
serverSettings,
config,
trustedSocketFactory,
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
oAuth2TokenProvider
serverSettings,
config,
trustedSocketFactory,
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
oAuth2TokenProvider
)
}

View file

@ -6,11 +6,12 @@ import org.koin.dsl.module
val backendsModule = module {
single {
BackendManager(
mapOf(
"imap" to get<ImapBackendFactory>(),
"pop3" to get<Pop3BackendFactory>(),
"webdav" to get<WebDavBackendFactory>()
))
mapOf(
"imap" to get<ImapBackendFactory>(),
"pop3" to get<Pop3BackendFactory>(),
"webdav" to get<WebDavBackendFactory>()
)
)
}
single { ImapBackendFactory(get(), get(), get(), get()) }
single { Pop3BackendFactory(get(), get()) }

View file

@ -27,34 +27,36 @@ class K9NotificationResourceProvider(private val context: Context) : Notificatio
get() = context.getString(R.string.notification_channel_miscellaneous_description)
override fun authenticationErrorTitle(): String =
context.getString(R.string.notification_authentication_error_title)
context.getString(R.string.notification_authentication_error_title)
override fun authenticationErrorBody(accountName: String): String =
context.getString(R.string.notification_authentication_error_text, accountName)
context.getString(R.string.notification_authentication_error_text, accountName)
override fun certificateErrorTitle(accountName: String): String =
context.getString(R.string.notification_certificate_error_title, accountName)
context.getString(R.string.notification_certificate_error_title, accountName)
override fun certificateErrorBody(): String = context.getString(R.string.notification_certificate_error_text)
override fun newMailTitle(): String = context.getString(R.string.notification_new_title)
override fun newMailUnreadMessageCount(unreadMessageCount: Int, accountName: String): String =
context.getString(R.string.notification_new_one_account_fmt, unreadMessageCount, accountName)
context.getString(R.string.notification_new_one_account_fmt, unreadMessageCount, accountName)
override fun newMessagesTitle(newMessagesCount: Int): String =
context.resources.getQuantityString(R.plurals.notification_new_messages_title,
newMessagesCount, newMessagesCount)
context.resources.getQuantityString(
R.plurals.notification_new_messages_title,
newMessagesCount, newMessagesCount
)
override fun additionalMessages(overflowMessagesCount: Int, accountName: String): String =
context.getString(R.string.notification_additional_messages, overflowMessagesCount, accountName)
context.getString(R.string.notification_additional_messages, overflowMessagesCount, accountName)
override fun previewEncrypted(): String = context.getString(R.string.preview_encrypted)
override fun noSubject(): String = context.getString(R.string.general_no_subject)
override fun recipientDisplayName(recipientDisplayName: String): String =
context.getString(R.string.message_to_fmt, recipientDisplayName)
context.getString(R.string.message_to_fmt, recipientDisplayName)
override fun noSender(): String = context.getString(R.string.general_no_sender)
@ -63,16 +65,16 @@ class K9NotificationResourceProvider(private val context: Context) : Notificatio
override fun sendingMailTitle(): String = context.getString(R.string.notification_bg_send_title)
override fun sendingMailBody(accountName: String): String =
context.getString(R.string.notification_bg_send_ticker, accountName)
context.getString(R.string.notification_bg_send_ticker, accountName)
override fun checkingMailTicker(accountName: String, folderName: String): String =
context.getString(R.string.notification_bg_sync_ticker, accountName, folderName)
context.getString(R.string.notification_bg_sync_ticker, accountName, folderName)
override fun checkingMailTitle(): String =
context.getString(R.string.notification_bg_sync_title)
context.getString(R.string.notification_bg_sync_title)
override fun checkingMailSeparator(): String =
context.getString(R.string.notification_bg_title_separator)
context.getString(R.string.notification_bg_title_separator)
override fun actionMarkAsRead(): String = context.getString(R.string.notification_action_mark_as_read)

View file

@ -9,10 +9,10 @@ class K9CoreResourceProvider(private val context: Context) : CoreResourceProvide
override fun defaultIdentityDescription(): String = context.getString(R.string.default_identity_description)
override fun internalStorageProviderName(): String =
context.getString(R.string.local_storage_provider_internal_label)
context.getString(R.string.local_storage_provider_internal_label)
override fun externalStorageProviderName(): String =
context.getString(R.string.local_storage_provider_external_label)
context.getString(R.string.local_storage_provider_external_label)
override fun contactDisplayNamePrefix(): String = context.getString(R.string.message_to_label)
override fun contactUnknownSender(): String = context.getString(R.string.unknown_sender)
@ -31,10 +31,10 @@ class K9CoreResourceProvider(private val context: Context) : CoreResourceProvide
override fun encryptedSubject(): String = context.getString(R.string.encrypted_subject)
override fun replyHeader(sender: String): String =
context.getString(R.string.message_compose_reply_header_fmt, sender)
context.getString(R.string.message_compose_reply_header_fmt, sender)
override fun replyHeader(sender: String, sentDate: String): String =
context.getString(R.string.message_compose_reply_header_fmt_with_date, sentDate, sender)
context.getString(R.string.message_compose_reply_header_fmt_with_date, sentDate, sender)
override fun searchAllMessagesTitle(): String = context.getString(R.string.search_all_messages_title)
override fun searchAllMessagesDetail(): String = context.getString(R.string.search_all_messages_detail)

View file

@ -4,14 +4,16 @@ import org.koin.dsl.module
val unreadWidgetModule = module {
single { UnreadWidgetRepository(context = get(), dataRetriever = get(), migrations = get()) }
single { UnreadWidgetDataProvider(
context = get(),
preferences = get(),
messagingController = get(),
defaultFolderProvider = get(),
folderRepositoryManager = get(),
folderNameFormatterFactory = get()
) }
single {
UnreadWidgetDataProvider(
context = get(),
preferences = get(),
messagingController = get(),
defaultFolderProvider = get(),
folderRepositoryManager = get(),
folderNameFormatterFactory = get()
)
}
single { UnreadWidgetUpdater(context = get()) }
single { UnreadWidgetUpdateListener(unreadWidgetUpdater = get()) }
single { UnreadWidgetMigrations(accountRepository = get(), folderRepositoryManager = get()) }

View file

@ -29,13 +29,16 @@ class UnreadWidgetDataProviderTest : AppRobolectricTest() {
val defaultFolderStrategy = createDefaultFolderStrategy()
val folderRepositoryManager = createFolderRepositoryManager()
val folderNameFormatterFactory = createFolderNameFormatterFactory()
val provider = UnreadWidgetDataProvider(context, preferences, messagingController, defaultFolderStrategy,
folderRepositoryManager, folderNameFormatterFactory)
val provider = UnreadWidgetDataProvider(
context, preferences, messagingController, defaultFolderStrategy,
folderRepositoryManager, folderNameFormatterFactory
)
@Test
fun unifiedInbox() {
val configuration = UnreadWidgetConfiguration(
appWidgetId = 1, accountUuid = SearchAccount.UNIFIED_INBOX, folderId = null)
appWidgetId = 1, accountUuid = SearchAccount.UNIFIED_INBOX, folderId = null
)
val widgetData = provider.loadUnreadWidgetData(configuration)
@ -48,7 +51,8 @@ class UnreadWidgetDataProviderTest : AppRobolectricTest() {
@Test
fun regularAccount() {
val configuration = UnreadWidgetConfiguration(
appWidgetId = 3, accountUuid = ACCOUNT_UUID, folderId = null)
appWidgetId = 3, accountUuid = ACCOUNT_UUID, folderId = null
)
val widgetData = provider.loadUnreadWidgetData(configuration)

View file

@ -5,24 +5,30 @@ import android.database.sqlite.SQLiteDatabase
internal object MigrationTo64 {
@JvmStatic
fun addExtraValuesTables(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE account_extra_values (" +
db.execSQL(
"CREATE TABLE account_extra_values (" +
"name TEXT NOT NULL PRIMARY KEY, " +
"value_text TEXT, " +
"value_integer INTEGER " +
")")
")"
)
db.execSQL("CREATE TABLE folder_extra_values (" +
db.execSQL(
"CREATE TABLE folder_extra_values (" +
"folder_id INTEGER NOT NULL, " +
"name TEXT NOT NULL, " +
"value_text TEXT, " +
"value_integer INTEGER, " +
"PRIMARY KEY (folder_id, name)" +
")")
")"
)
db.execSQL("CREATE TRIGGER delete_folder_extra_values " +
db.execSQL(
"CREATE TRIGGER delete_folder_extra_values " +
"BEFORE DELETE ON folders " +
"BEGIN " +
"DELETE FROM folder_extra_values WHERE old.id = folder_id; " +
"END;")
"END;"
)
}
}

View file

@ -10,16 +10,19 @@ internal object MigrationTo68 {
}
private fun createOutboxStateTable(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE outbox_state (" +
db.execSQL(
"CREATE TABLE outbox_state (" +
"message_id INTEGER PRIMARY KEY NOT NULL REFERENCES messages(id) ON DELETE CASCADE," +
"send_state TEXT," +
"number_of_send_attempts INTEGER DEFAULT 0," +
"error_timestamp INTEGER DEFAULT 0," +
"error TEXT)")
"error TEXT)"
)
}
private fun createOutboxStateEntries(db: SQLiteDatabase) {
db.execSQL("""
db.execSQL(
"""
INSERT INTO outbox_state (message_id, send_state)
SELECT messages.id, 'ready' FROM folders
JOIN messages ON (folders.id = messages.folder_id)

View file

@ -4,13 +4,21 @@ import android.database.sqlite.SQLiteDatabase
internal class MigrationTo71(private val db: SQLiteDatabase) {
fun cleanUpFolderClass() {
db.execSQL("UPDATE folders SET poll_class = 'NO_CLASS' " +
"WHERE poll_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')")
db.execSQL("UPDATE folders SET push_class = 'NO_CLASS' " +
"WHERE push_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')")
db.execSQL("UPDATE folders SET display_class = 'NO_CLASS' " +
"WHERE display_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')")
db.execSQL("UPDATE folders SET notify_class = 'NO_CLASS' " +
"WHERE notify_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')")
db.execSQL(
"UPDATE folders SET poll_class = 'NO_CLASS' " +
"WHERE poll_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')"
)
db.execSQL(
"UPDATE folders SET push_class = 'NO_CLASS' " +
"WHERE push_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')"
)
db.execSQL(
"UPDATE folders SET display_class = 'NO_CLASS' " +
"WHERE display_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')"
)
db.execSQL(
"UPDATE folders SET notify_class = 'NO_CLASS' " +
"WHERE notify_class NOT IN ('NO_CLASS', 'INHERITED', 'FIRST_CLASS', 'SECOND_CLASS')"
)
}
}

View file

@ -23,7 +23,7 @@ class StorageEditorTest : K9RobolectricTest() {
private val workingMap = mutableMapOf<String, String>()
private val storageMap = mapOf(
"storage-key" to "storage-value"
"storage-key" to "storage-value"
)
@Before

View file

@ -37,11 +37,11 @@ class StoragePersisterTest : K9RobolectricTest() {
@Test
fun doInTransaction_put() {
val operationCallback = prepareCallback(
persistOp = { ops -> ops.put("x", "y") },
onSuccess = { map ->
assertEquals(1, map.size)
assertEquals("y", map["x"])
}
persistOp = { ops -> ops.put("x", "y") },
onSuccess = { map ->
assertEquals(1, map.size)
assertEquals("y", map["x"])
}
)
storagePersister.doInTransaction(operationCallback)
@ -55,10 +55,10 @@ class StoragePersisterTest : K9RobolectricTest() {
fun doInTransaction_putAndThrow() {
val exception = Exception("boom")
val operationCallback = prepareCallback(
persistOp = { ops ->
ops.put("x", "y")
throw exception
}
persistOp = { ops ->
ops.put("x", "y")
throw exception
}
)
try {
@ -76,9 +76,9 @@ class StoragePersisterTest : K9RobolectricTest() {
@Test
fun doInTransaction_remove() {
val operationCallback = prepareCallback(
before = { map -> map["x"] = "y" },
persistOp = { ops -> ops.remove("x") },
onSuccess = { map -> assertTrue(map.isEmpty()) }
before = { map -> map["x"] = "y" },
persistOp = { ops -> ops.remove("x") },
onSuccess = { map -> assertTrue(map.isEmpty()) }
)
storagePersister.doInTransaction(operationCallback)
@ -90,8 +90,8 @@ class StoragePersisterTest : K9RobolectricTest() {
@Test
fun doInTransaction_before_preserveButNotPersist() {
val operationCallback = prepareCallback(
before = { map -> map["x"] = "y" },
onSuccess = { map -> assertEquals("y", map["x"]) }
before = { map -> map["x"] = "y" },
onSuccess = { map -> assertEquals("y", map["x"]) }
)
storagePersister.doInTransaction(operationCallback)

View file

@ -6,9 +6,9 @@ import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import org.junit.Assert.fail as junitFail
import org.junit.Test
import org.mockito.ArgumentMatchers.anyLong
import org.junit.Assert.fail as junitFail
private const val SOURCE_FOLDER_ID = 3L
private const val DESTINATION_FOLDER_ID = 23L

View file

@ -14,7 +14,7 @@ class AccountRemoverService : JobIntentService(), KoinComponent {
override fun onHandleWork(intent: Intent) {
val accountUuid = intent.getStringExtra(ARG_ACCOUNT_UUID)
?: throw IllegalArgumentException("No account UUID provided")
?: throw IllegalArgumentException("No account UUID provided")
accountRemover.removeAccount(accountUuid)
}

View file

@ -38,9 +38,10 @@ data class ComposeCryptoStatus(
isEncryptSubject: Boolean,
cryptoMode: CryptoMode
) : this(
openPgpProviderState, openPgpKeyId,
recipientAddresses.map { it.address.address },
isPgpInlineModeEnabled, isSenderPreferEncryptMutual, isReplyToEncrypted, isEncryptAllDrafts, isEncryptSubject, cryptoMode)
openPgpProviderState, openPgpKeyId,
recipientAddresses.map { it.address.address },
isPgpInlineModeEnabled, isSenderPreferEncryptMutual, isReplyToEncrypted, isEncryptAllDrafts, isEncryptSubject, cryptoMode
)
private val recipientAutocryptStatusType = recipientAutocryptStatus?.type
private val isRecipientsPreferEncryptMutual = recipientAutocryptStatus?.type?.isMutual ?: false
@ -99,12 +100,12 @@ data class ComposeCryptoStatus(
}
val displayType =
displayTypeFromProviderError
?: displayTypeFromAutocryptError
?: displayTypeFromEnabledAutocryptStatus
?: displayTypeFromSignOnly
?: displayTypeFromEncryptionAvailable
?: CryptoStatusDisplayType.UNAVAILABLE
displayTypeFromProviderError
?: displayTypeFromAutocryptError
?: displayTypeFromEnabledAutocryptStatus
?: displayTypeFromSignOnly
?: displayTypeFromEncryptionAvailable
?: CryptoStatusDisplayType.UNAVAILABLE
val specialModeDisplayType = when {
openPgpProviderState != OpenPgpProviderState.OK -> CryptoSpecialModeDisplayType.NONE
@ -140,16 +141,16 @@ data class ComposeCryptoStatus(
override fun getRecipientAddresses() = recipientAddresses.toTypedArray()
fun withRecipientAutocryptStatus(recipientAutocryptStatusType: RecipientAutocryptStatus) = ComposeCryptoStatus(
openPgpProviderState = openPgpProviderState,
cryptoMode = cryptoMode,
openPgpKeyId = openPgpKeyId,
isPgpInlineModeEnabled = isPgpInlineModeEnabled,
isSenderPreferEncryptMutual = isSenderPreferEncryptMutual,
isReplyToEncrypted = isReplyToEncrypted,
isEncryptAllDrafts = isEncryptAllDrafts,
isEncryptSubject = isEncryptSubject,
recipientAddresses = recipientAddresses,
recipientAutocryptStatus = recipientAutocryptStatusType
openPgpProviderState = openPgpProviderState,
cryptoMode = cryptoMode,
openPgpKeyId = openPgpKeyId,
isPgpInlineModeEnabled = isPgpInlineModeEnabled,
isSenderPreferEncryptMutual = isSenderPreferEncryptMutual,
isReplyToEncrypted = isReplyToEncrypted,
isEncryptAllDrafts = isEncryptAllDrafts,
isEncryptSubject = isEncryptSubject,
recipientAddresses = recipientAddresses,
recipientAutocryptStatus = recipientAutocryptStatusType
)
enum class SendErrorState {

View file

@ -33,9 +33,11 @@ class ContactLetterBitmapCreator(
paint.getTextBounds(letter, 0, 1, rect)
val width = paint.measureText(letter)
canvas.drawText(letter,
pictureSizeInPx / 2f - width / 2f,
pictureSizeInPx / 2f + rect.height() / 2f, paint)
canvas.drawText(
letter,
pictureSizeInPx / 2f - width / 2f,
pictureSizeInPx / 2f + rect.height() / 2f, paint
)
return bitmap
}
@ -61,37 +63,39 @@ class ContactLetterBitmapCreator(
companion object {
private val BACKGROUND_COLORS_LIGHT = intArrayOf(
MaterialColors.RED_300,
MaterialColors.DEEP_PURPLE_300,
MaterialColors.LIGHT_BLUE_300,
MaterialColors.GREEN_300,
MaterialColors.DEEP_ORANGE_300,
MaterialColors.BLUE_GREY_300,
MaterialColors.PINK_300,
MaterialColors.INDIGO_300,
MaterialColors.CYAN_300,
MaterialColors.AMBER_400,
MaterialColors.BROWN_300,
MaterialColors.PURPLE_300,
MaterialColors.BLUE_300,
MaterialColors.TEAL_300,
MaterialColors.ORANGE_400)
MaterialColors.RED_300,
MaterialColors.DEEP_PURPLE_300,
MaterialColors.LIGHT_BLUE_300,
MaterialColors.GREEN_300,
MaterialColors.DEEP_ORANGE_300,
MaterialColors.BLUE_GREY_300,
MaterialColors.PINK_300,
MaterialColors.INDIGO_300,
MaterialColors.CYAN_300,
MaterialColors.AMBER_400,
MaterialColors.BROWN_300,
MaterialColors.PURPLE_300,
MaterialColors.BLUE_300,
MaterialColors.TEAL_300,
MaterialColors.ORANGE_400
)
private val BACKGROUND_COLORS_DARK = intArrayOf(
MaterialColors.RED_600,
MaterialColors.DEEP_PURPLE_600,
MaterialColors.LIGHT_BLUE_600,
MaterialColors.GREEN_600,
MaterialColors.DEEP_ORANGE_600,
MaterialColors.BLUE_GREY_600,
MaterialColors.PINK_600,
MaterialColors.INDIGO_600,
MaterialColors.CYAN_600,
MaterialColors.AMBER_600,
MaterialColors.BROWN_600,
MaterialColors.PURPLE_600,
MaterialColors.BLUE_600,
MaterialColors.TEAL_600,
MaterialColors.ORANGE_600)
MaterialColors.RED_600,
MaterialColors.DEEP_PURPLE_600,
MaterialColors.LIGHT_BLUE_600,
MaterialColors.GREEN_600,
MaterialColors.DEEP_ORANGE_600,
MaterialColors.BLUE_GREY_600,
MaterialColors.PINK_600,
MaterialColors.INDIGO_600,
MaterialColors.CYAN_600,
MaterialColors.AMBER_600,
MaterialColors.BROWN_600,
MaterialColors.PURPLE_600,
MaterialColors.BLUE_600,
MaterialColors.TEAL_600,
MaterialColors.ORANGE_600
)
}
}

View file

@ -172,7 +172,7 @@ class MessageListAdapter internal constructor(
val beforePreviewText = if (appearance.senderAboveSubject) subject else displayName
val sigil = recipientSigil(toMe, ccMe)
val messageStringBuilder = SpannableStringBuilder(sigil)
.append(beforePreviewText)
.append(beforePreviewText)
if (appearance.previewLines > 0) {
val preview = getPreview(isMessageEncrypted, previewText)
messageStringBuilder.append(" ").append(preview)
@ -214,10 +214,10 @@ class MessageListAdapter internal constructor(
// Set span (color) for preview message
previewText.setSpan(
ForegroundColorSpan(previewTextColor),
beforePreviewLength,
previewText.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
ForegroundColorSpan(previewTextColor),
beforePreviewLength,
previewText.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}

View file

@ -75,12 +75,12 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
accountHeader = buildAccountHeader()
drawer = DrawerBuilder()
.withActivity(parent)
.withOnDrawerItemClickListener(createItemClickListener())
.withOnDrawerListener(parent.createOnDrawerListener())
.withSavedInstance(savedInstanceState)
.withAccountHeader(accountHeader)
.build()
.withActivity(parent)
.withOnDrawerItemClickListener(createItemClickListener())
.withOnDrawerListener(parent.createOnDrawerListener())
.withSavedInstance(savedInstanceState)
.withAccountHeader(accountHeader)
.build()
swipeRefreshLayout = drawer.slider.findViewById(R.id.material_drawer_swipe_refresh)
accountHeader.view.addOnLayoutChangeListener { view, _, _, _, _, _, _, _, _ ->
@ -103,19 +103,22 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
private fun buildAccountHeader(): AccountHeader {
val headerBuilder = AccountHeaderBuilder()
.withActivity(parent)
.withHeaderBackground(R.drawable.drawer_header_background)
.withActivity(parent)
.withHeaderBackground(R.drawable.drawer_header_background)
if (!K9.isHideSpecialAccounts) {
headerBuilder.addProfiles(ProfileDrawerItem()
headerBuilder.addProfiles(
ProfileDrawerItem()
.withNameShown(true)
.withName(R.string.integrated_inbox_title)
.withEmail(parent.getString(R.string.integrated_inbox_detail))
.withIcon(IconicsDrawable(parent, FontAwesome.Icon.faw_users)
.withIcon(
IconicsDrawable(parent, FontAwesome.Icon.faw_users)
.colorRes(R.color.material_drawer_background)
.backgroundColor(IconicsColor.colorInt(Color.GRAY))
.size(IconicsSize.dp(56))
.padding(IconicsSize.dp(8)))
.padding(IconicsSize.dp(8))
)
.withSelected(unifiedInboxSelected)
.withIdentifier(DRAWER_ID_UNIFIED_INBOX)
)
@ -127,19 +130,20 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
val drawerId = (account.accountNumber + 1 shl DRAWER_ACCOUNT_SHIFT).toLong()
val pdi = ProfileDrawerItem()
.withNameShown(true)
.withName(account.description)
.withEmail(account.email)
.withIdentifier(drawerId)
.withSelected(false)
.withTag(account)
.withNameShown(true)
.withName(account.description)
.withEmail(account.email)
.withIdentifier(drawerId)
.withSelected(false)
.withTag(account)
val photoUri = Contacts.getInstance(parent).getPhotoUri(account.email)
if (photoUri != null && !photoUris.contains(photoUri)) {
photoUris.add(photoUri)
pdi.withIcon(photoUri)
} else {
pdi.withIcon(IconicsDrawable(parent, FontAwesome.Icon.faw_user_alt)
pdi.withIcon(
IconicsDrawable(parent, FontAwesome.Icon.faw_user_alt)
.colorRes(R.color.material_drawer_background)
.backgroundColor(IconicsColor.colorInt(account.chipColor))
.size(IconicsSize.dp(56))
@ -150,20 +154,20 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
}
return headerBuilder
.withOnAccountHeaderListener(object : AccountHeader.OnAccountHeaderListener {
override fun onProfileChanged(view: View?, profile: IProfile<*>, current: Boolean): Boolean {
if (profile.identifier == DRAWER_ID_UNIFIED_INBOX) {
parent.openUnifiedInbox()
return false
} else {
val account = (profile as ProfileDrawerItem).tag as Account
parent.openRealAccount(account)
updateUserAccountsAndFolders(account)
return true
}
.withOnAccountHeaderListener(object : AccountHeader.OnAccountHeaderListener {
override fun onProfileChanged(view: View?, profile: IProfile<*>, current: Boolean): Boolean {
if (profile.identifier == DRAWER_ID_UNIFIED_INBOX) {
parent.openUnifiedInbox()
return false
} else {
val account = (profile as ProfileDrawerItem).tag as Account
parent.openRealAccount(account)
updateUserAccountsAndFolders(account)
return true
}
})
.build()
}
})
.build()
}
private fun addFooterItems() {
@ -177,11 +181,12 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
)
}
drawer.addStickyFooterItem(PrimaryDrawerItem()
.withName(R.string.preferences_action)
.withIcon(getResId(R.attr.iconActionSettings))
.withIdentifier(DRAWER_ID_PREFERENCES)
.withSelectable(false)
drawer.addStickyFooterItem(
PrimaryDrawerItem()
.withName(R.string.preferences_action)
.withIcon(getResId(R.attr.iconActionSettings))
.withIdentifier(DRAWER_ID_PREFERENCES)
.withSelectable(false)
)
}
@ -218,11 +223,14 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
// Account can be null to refresh all (unified inbox or account list).
swipeRefreshLayout.setOnRefreshListener {
val accountToRefresh = if (accountHeader.isSelectionListShown) null else account
messagingController.checkMail(parent, accountToRefresh, true, true, object : SimpleMessagingListener() {
override fun checkMailFinished(context: Context?, account: Account?) {
swipeRefreshLayout.isRefreshing = false
messagingController.checkMail(
parent, accountToRefresh, true, true,
object : SimpleMessagingListener() {
override fun checkMailFinished(context: Context?, account: Account?) {
swipeRefreshLayout.isRefreshing = false
}
}
})
)
}
}
@ -260,12 +268,12 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
val drawerId = folder.id shl DRAWER_FOLDER_SHIFT
val drawerItem = PrimaryDrawerItem()
.withIcon(folderIconProvider.getFolderIcon(folder.type))
.withIdentifier(drawerId)
.withTag(folder)
.withSelectedColor(selectedColor)
.withSelectedTextColor(accentColor)
.withName(getFolderDisplayName(folder))
.withIcon(folderIconProvider.getFolderIcon(folder.type))
.withIdentifier(drawerId)
.withTag(folder)
.withSelectedColor(selectedColor)
.withSelectedTextColor(accentColor)
.withName(getFolderDisplayName(folder))
val unreadCount = displayFolder.unreadCount
if (unreadCount > 0) {

View file

@ -119,14 +119,17 @@ class SimpleHighlightView private constructor(context: Context, style: Int) : Fr
}
private fun setTarget(targetView: View) {
postDelayed({
if (canUpdateBitmap()) {
updateBitmap()
}
postDelayed(
{
if (canUpdateBitmap()) {
updateBitmap()
}
val point = targetView.getHighlightPoint()
setHighlightPosition(point.x, point.y)
}, 100)
val point = targetView.getHighlightPoint()
setHighlightPosition(point.x, point.y)
},
100
)
}
private fun canUpdateBitmap(): Boolean {

View file

@ -37,17 +37,20 @@ class AutocryptKeyTransferPresenter internal constructor(
account = preferences.getAccount(accountUuid)
openPgpApiManager.setOpenPgpProvider(account.openPgpProvider, object : OpenPgpApiManagerCallback {
override fun onOpenPgpProviderStatusChanged() {
if (openPgpApiManager.openPgpProviderState == OpenPgpApiManager.OpenPgpProviderState.UI_REQUIRED) {
openPgpApiManager.setOpenPgpProvider(
account.openPgpProvider,
object : OpenPgpApiManagerCallback {
override fun onOpenPgpProviderStatusChanged() {
if (openPgpApiManager.openPgpProviderState == OpenPgpApiManager.OpenPgpProviderState.UI_REQUIRED) {
view.finishWithProviderConnectError(openPgpApiManager.readableOpenPgpProviderName)
}
}
override fun onOpenPgpProviderError(error: OpenPgpProviderError) {
view.finishWithProviderConnectError(openPgpApiManager.readableOpenPgpProviderName)
}
}
override fun onOpenPgpProviderError(error: OpenPgpProviderError) {
view.finishWithProviderConnectError(openPgpApiManager.readableOpenPgpProviderName)
}
})
)
view.setAddress(account.identities[0].email!!)

View file

@ -10,11 +10,11 @@ val endToEndUiModule = module {
factory { AutocryptSetupTransferLiveEvent(get()) }
factory { (lifecycleOwner: LifecycleOwner, autocryptTransferView: AutocryptKeyTransferActivity) ->
AutocryptKeyTransferPresenter(
lifecycleOwner,
get { parametersOf(lifecycleOwner) },
get(),
get(),
autocryptTransferView
lifecycleOwner,
get { parametersOf(lifecycleOwner) },
get(),
get(),
autocryptTransferView
)
}
viewModel { AutocryptKeyTransferViewModel(get(), get()) }

View file

@ -2,9 +2,9 @@ package com.fsck.k9.ui.folders
import android.content.res.Resources
import android.util.TypedValue
import com.fsck.k9.mail.FolderType as LegacyFolderType
import com.fsck.k9.mailstore.FolderType
import com.fsck.k9.ui.R
import com.fsck.k9.mail.FolderType as LegacyFolderType
class FolderIconProvider(private val theme: Resources.Theme) {
private val iconFolderInboxResId: Int

View file

@ -7,12 +7,12 @@ import com.fsck.k9.ui.base.ThemeManager
class HtmlSettingsProvider(private val themeManager: ThemeManager) {
fun createForMessageView() = HtmlSettings(
useDarkMode = themeManager.messageViewTheme == Theme.DARK,
useFixedWidthFont = K9.isUseMessageViewFixedWidthFont
useDarkMode = themeManager.messageViewTheme == Theme.DARK,
useFixedWidthFont = K9.isUseMessageViewFixedWidthFont
)
fun createForMessageCompose() = HtmlSettings(
useDarkMode = themeManager.messageComposeTheme == Theme.DARK,
useFixedWidthFont = false
useDarkMode = themeManager.messageComposeTheme == Theme.DARK,
useFixedWidthFont = false
)
}

View file

@ -96,11 +96,11 @@ class FolderSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFra
private fun showClearFolderConfirmationDialog() {
val dialogFragment = ConfirmationDialogFragment.newInstance(
DIALOG_CLEAR_FOLDER,
getString(R.string.dialog_confirm_clear_local_folder_title),
getString(R.string.dialog_confirm_clear_local_folder_message),
getString(R.string.dialog_confirm_clear_local_folder_action),
getString(R.string.cancel_action)
DIALOG_CLEAR_FOLDER,
getString(R.string.dialog_confirm_clear_local_folder_title),
getString(R.string.dialog_confirm_clear_local_folder_message),
getString(R.string.dialog_confirm_clear_local_folder_action),
getString(R.string.cancel_action)
)
dialogFragment.setTargetFragment(this, REQUEST_CLEAR_FOLDER)
dialogFragment.show(requireFragmentManager(), TAG_CLEAR_FOLDER_CONFIRMATION)

View file

@ -25,8 +25,10 @@ class PermissionRationaleDialogFragment : DialogFragment() {
.setMessage(permission.rationaleMessage)
.setPositiveButton(R.string.okay_action) { _, _ ->
val permissionUiHelper = requireActivity() as? PermissionUiHelper
?: throw AssertionError("Activities using PermissionRationaleDialogFragment need to " +
"implement PermissionUiHelper")
?: throw AssertionError(
"Activities using PermissionRationaleDialogFragment need to " +
"implement PermissionUiHelper"
)
permissionUiHelper.requestPermission(permission)
}.create()

View file

@ -106,7 +106,7 @@ class AboutFragment : Fragment() {
}
private class LibrariesAdapter(private val dataset: Array<Library>) :
RecyclerView.Adapter<LibrariesAdapter.ViewHolder>() {
RecyclerView.Adapter<LibrariesAdapter.ViewHolder>() {
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)

View file

@ -59,9 +59,9 @@ class SettingsListFragment : Fragment() {
val generalSection = Section().apply {
val generalSettingsActionItem = SettingsActionItem(
getString(R.string.general_settings_title),
R.id.action_settingsListScreen_to_generalSettingsScreen,
R.attr.iconSettingsGeneral
getString(R.string.general_settings_title),
R.id.action_settingsListScreen_to_generalSettingsScreen,
R.attr.iconSettingsGeneral
)
add(generalSettingsActionItem)
}
@ -73,9 +73,9 @@ class SettingsListFragment : Fragment() {
}
val addAccountActionItem = SettingsActionItem(
getString(R.string.add_account_action),
R.id.action_settingsListScreen_to_addAccountScreen,
R.attr.iconSettingsAccountAdd
getString(R.string.add_account_action),
R.id.action_settingsListScreen_to_addAccountScreen,
R.attr.iconSettingsAccountAdd
)
add(addAccountActionItem)
@ -86,16 +86,16 @@ class SettingsListFragment : Fragment() {
val backupSection = Section().apply {
val exportSettingsActionItem = SettingsActionItem(
getString(R.string.settings_export_title),
R.id.action_settingsListScreen_to_settingsExportScreen,
R.attr.iconSettingsExport
getString(R.string.settings_export_title),
R.id.action_settingsListScreen_to_settingsExportScreen,
R.attr.iconSettingsExport
)
add(exportSettingsActionItem)
val importSettingsActionItem = SettingsActionItem(
getString(R.string.settings_import_title),
R.id.action_settingsListScreen_to_settingsImportScreen,
R.attr.iconSettingsImport
getString(R.string.settings_import_title),
R.id.action_settingsListScreen_to_settingsImportScreen,
R.attr.iconSettingsImport
)
add(importSettingsActionItem)
}

View file

@ -69,7 +69,7 @@ class AccountSelectionSpinner : Spinner {
val account = getItem(position) ?: error("No item at position $position")
val view = convertView
?: LayoutInflater.from(context).inflate(R.layout.account_spinner_dropdown_item, parent, false)
?: LayoutInflater.from(context).inflate(R.layout.account_spinner_dropdown_item, parent, false)
return view.apply {
name.text = account.description

View file

@ -317,11 +317,11 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
private fun onDeleteAccount() {
val dialogFragment = ConfirmationDialogFragment.newInstance(
DIALOG_DELETE_ACCOUNT,
getString(R.string.account_delete_dlg_title),
getString(R.string.account_delete_dlg_instructions_fmt, getAccount().description),
getString(R.string.okay_action),
getString(R.string.cancel_action)
DIALOG_DELETE_ACCOUNT,
getString(R.string.account_delete_dlg_title),
getString(R.string.account_delete_dlg_instructions_fmt, getAccount().description),
getString(R.string.okay_action),
getString(R.string.cancel_action)
)
dialogFragment.setTargetFragment(this, REQUEST_DELETE_ACCOUNT)
dialogFragment.show(requireFragmentManager(), TAG_DELETE_ACCOUNT_CONFIRMATION)
@ -380,12 +380,12 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
private const val DELETE_POLICY_MARK_AS_READ = "MARK_AS_READ"
private val PRE_SDK26_NOTIFICATION_PREFERENCES = arrayOf(
"account_ringtone",
"account_vibrate",
"account_vibrate_pattern",
"account_vibrate_times",
"account_led",
"led_color"
"account_ringtone",
"account_vibrate",
"account_vibrate_pattern",
"account_vibrate_times",
"account_led",
"led_color"
)
private const val DIALOG_DELETE_ACCOUNT = 1
@ -393,7 +393,8 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
private const val TAG_DELETE_ACCOUNT_CONFIRMATION = "delete_account_confirmation"
fun create(accountUuid: String, rootKey: String?) = AccountSettingsFragment().withArguments(
ARG_ACCOUNT_UUID to accountUuid,
PreferenceFragmentCompat.ARG_PREFERENCE_ROOT to rootKey)
ARG_ACCOUNT_UUID to accountUuid,
PreferenceFragmentCompat.ARG_PREFERENCE_ROOT to rootKey
)
}
}

View file

@ -37,11 +37,11 @@ class AutocryptPreferEncryptDialogFragment : DialogFragment() {
}
return AlertDialog.Builder(requireContext())
// TODO add autocrypt logo?
// .setIcon(R.drawable.autocrypt)
.setView(view)
.setPositiveButton(R.string.done_action, null)
.create()
// TODO add autocrypt logo?
// .setIcon(R.drawable.autocrypt)
.setView(view)
.setPositiveButton(R.string.done_action, null)
.create()
}
private fun TextView.makeLinksClickable() {

View file

@ -16,8 +16,10 @@ class AutocryptPreferEncryptPreference
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = TypedArrayUtils.getAttr(context, androidx.preference.R.attr.preferenceStyle,
android.R.attr.preferenceStyle),
defStyleAttr: Int = TypedArrayUtils.getAttr(
context, androidx.preference.R.attr.preferenceStyle,
android.R.attr.preferenceStyle
),
defStyleRes: Int = 0
) : TwoStatePreference(context, attrs, defStyleAttr, defStyleRes) {
@ -46,7 +48,8 @@ constructor(
companion object {
init {
PreferenceFragmentCompat.registerPreferenceFragment(
AutocryptPreferEncryptPreference::class.java, AutocryptPreferEncryptDialogFragment::class.java)
AutocryptPreferEncryptPreference::class.java, AutocryptPreferEncryptDialogFragment::class.java
)
}
}
}

View file

@ -24,8 +24,10 @@ class FolderListPreference
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = TypedArrayUtils.getAttr(context, androidx.preference.R.attr.dialogPreferenceStyle,
android.R.attr.dialogPreferenceStyle),
defStyleAttr: Int = TypedArrayUtils.getAttr(
context, androidx.preference.R.attr.dialogPreferenceStyle,
android.R.attr.dialogPreferenceStyle
),
defStyleRes: Int = 0
) : ListPreference(context, attrs, defStyleAttr, defStyleRes), KoinComponent {
private val folderNameFormatter: FolderNameFormatter by inject { parametersOf(context) }
@ -52,8 +54,10 @@ constructor(
}
val automaticFolderValue = AUTOMATIC_PREFIX + (automaticFolder?.id?.toString() ?: NO_FOLDER_VALUE)
automaticFolderOption = context.getString(R.string.account_settings_automatic_special_folder,
automaticFolderName).italicize()
automaticFolderOption = context.getString(
R.string.account_settings_automatic_special_folder,
automaticFolderName
).italicize()
entries = (listOf(automaticFolderOption) + noFolderSelectedName + getFolderDisplayNames(folders)).toTypedArray()
entryValues = (listOf(automaticFolderValue) + NO_FOLDER_SELECTED_VALUE + getFolderValues(folders)).toTypedArray()

View file

@ -17,8 +17,10 @@ class NotificationsPreference
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = TypedArrayUtils.getAttr(context, androidx.preference.R.attr.preferenceStyle,
android.R.attr.preferenceStyle),
defStyleAttr: Int = TypedArrayUtils.getAttr(
context, androidx.preference.R.attr.preferenceStyle,
android.R.attr.preferenceStyle
),
defStyleRes: Int = 0
) : Preference(context, attrs, defStyleAttr, defStyleRes) {
@ -31,7 +33,8 @@ constructor(
companion object {
init {
PreferenceFragmentCompat.registerPreferenceFragment(
NotificationsPreference::class.java, DialogFragment::class.java)
NotificationsPreference::class.java, DialogFragment::class.java
)
}
}
}

View file

@ -48,9 +48,11 @@ class SettingsExportFragment : Fragment() {
viewModel.onSettingsListItemSelected(position, !item.isSelected)
true
}
addEventHook(CheckBoxClickEvent { position, isSelected ->
viewModel.onSettingsListItemSelected(position, isSelected)
})
addEventHook(
CheckBoxClickEvent { position, isSelected ->
viewModel.onSettingsListItemSelected(position, isSelected)
}
)
}
val recyclerView = view.findViewById<RecyclerView>(R.id.settingsExportList)

View file

@ -35,19 +35,19 @@ class SettingsExportViewModel(
private val includeGeneralSettings: Boolean
get() {
return savedSelection?.includeGeneralSettings
?: uiModel.settingsList.first { it is SettingsListItem.GeneralSettings }.selected
?: uiModel.settingsList.first { it is SettingsListItem.GeneralSettings }.selected
}
private val selectedAccounts: Set<AccountUuid>
get() {
return savedSelection?.selectedAccountUuids
?: uiModel.settingsList.asSequence()
.filterIsInstance<SettingsListItem.Account>()
.filter { it.selected }
.map {
accountsMap[it.accountNumber] ?: error("Unknown account number: ${it.accountNumber}")
}
.toSet()
?: uiModel.settingsList.asSequence()
.filterIsInstance<SettingsListItem.Account>()
.filter { it.selected }
.map {
accountsMap[it.accountNumber] ?: error("Unknown account number: ${it.accountNumber}")
}
.toSet()
}
fun getActionEvents(): LiveData<Action> = actionLiveData
@ -86,14 +86,14 @@ class SettingsExportViewModel(
fun initializeFromSavedState(savedInstanceState: Bundle) {
savedSelection = SavedListItemSelection(
includeGeneralSettings = savedInstanceState.getBoolean(STATE_INCLUDE_GENERAL_SETTINGS),
selectedAccountUuids = savedInstanceState.getStringArray(STATE_SELECTED_ACCOUNTS)?.toSet() ?: emptySet()
includeGeneralSettings = savedInstanceState.getBoolean(STATE_INCLUDE_GENERAL_SETTINGS),
selectedAccountUuids = savedInstanceState.getStringArray(STATE_SELECTED_ACCOUNTS)?.toSet() ?: emptySet()
)
uiModel.apply {
isSettingsListEnabled = savedInstanceState.getBoolean(STATE_SETTINGS_LIST_ENABLED)
exportButton = ButtonState.valueOf(
savedInstanceState.getString(STATE_EXPORT_BUTTON, ButtonState.DISABLED.name)
savedInstanceState.getString(STATE_EXPORT_BUTTON, ButtonState.DISABLED.name)
)
isShareButtonVisible = savedInstanceState.getBoolean(STATE_SHARE_BUTTON_VISIBLE)
isProgressVisible = savedInstanceState.getBoolean(STATE_PROGRESS_VISIBLE)

View file

@ -13,9 +13,11 @@ class LanguagePreference
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = TypedArrayUtils.getAttr(context,
androidx.preference.R.attr.dialogPreferenceStyle,
android.R.attr.dialogPreferenceStyle),
defStyleAttr: Int = TypedArrayUtils.getAttr(
context,
androidx.preference.R.attr.dialogPreferenceStyle,
android.R.attr.dialogPreferenceStyle
),
defStyleRes: Int = 0
) : ListPreference(context, attrs, defStyleAttr, defStyleRes) {

View file

@ -36,18 +36,18 @@ class PasswordPromptDialogFragment : DialogFragment() {
}
dialogView = createView(
accountName,
inputIncomingServerPassword,
incomingServerName,
inputOutgoingServerPassword,
outgoingServerName
accountName,
inputIncomingServerPassword,
incomingServerName,
inputOutgoingServerPassword,
outgoingServerName
)
return AlertDialog.Builder(requireContext())
.setView(dialogView)
.setPositiveButton(R.string.okay_action) { _, _ -> deliverPasswordPromptResult() }
.setNegativeButton(R.string.cancel_action, null)
.create()
.setView(dialogView)
.setPositiveButton(R.string.okay_action) { _, _ -> deliverPasswordPromptResult() }
.setNegativeButton(R.string.cancel_action, null)
.create()
}
@SuppressLint("InflateParams")
@ -63,9 +63,9 @@ class PasswordPromptDialogFragment : DialogFragment() {
val quantity = if (inputIncomingServerPassword && inputOutgoingServerPassword) 2 else 1
view.passwordPromptIntro.text = resources.getQuantityString(
R.plurals.settings_import_password_prompt,
quantity,
accountName
R.plurals.settings_import_password_prompt,
quantity,
accountName
)
view.incomingServerName.text = getString(R.string.server_name_format, incomingServerName)
view.outgoingServerName.text = getString(R.string.server_name_format, outgoingServerName)
@ -125,12 +125,12 @@ class PasswordPromptDialogFragment : DialogFragment() {
requestCode: Int
) = PasswordPromptDialogFragment().apply {
arguments = bundleOf(
ARG_ACCOUNT_UUID to accountUuid,
ARG_ACCOUNT_NAME to accountName,
ARG_INPUT_INCOMING_SERVER_PASSWORD to inputIncomingServerPassword,
ARG_INCOMING_SERVER_NAME to incomingServerName,
ARG_INPUT_OUTGOING_SERVER_PASSWORD to inputOutgoingServerPassword,
ARG_OUTGOING_SERVER_NAME to outgoingServerName
ARG_ACCOUNT_UUID to accountUuid,
ARG_ACCOUNT_NAME to accountName,
ARG_INPUT_INCOMING_SERVER_PASSWORD to inputIncomingServerPassword,
ARG_INCOMING_SERVER_NAME to incomingServerName,
ARG_INPUT_OUTGOING_SERVER_PASSWORD to inputOutgoingServerPassword,
ARG_OUTGOING_SERVER_NAME to outgoingServerName
)
setTargetFragment(targetFragment, requestCode)
}

View file

@ -53,9 +53,11 @@ class SettingsImportFragment : Fragment() {
viewModel.onSettingsListItemClicked(position)
true
}
addEventHook(ImportListItemClickEvent { position ->
viewModel.onSettingsListItemClicked(position)
})
addEventHook(
ImportListItemClickEvent { position ->
viewModel.onSettingsListItemClicked(position)
}
)
}
val recyclerView = view.findViewById<RecyclerView>(R.id.settingsImportList)
@ -158,14 +160,14 @@ class SettingsImportFragment : Fragment() {
private fun showPasswordPrompt(action: Action.PasswordPrompt) {
val dialogFragment = PasswordPromptDialogFragment.create(
action.accountUuid,
action.accountName,
action.inputIncomingServerPassword,
action.incomingServerName,
action.inputOutgoingServerPassword,
action.outgoingServerName,
targetFragment = this,
requestCode = REQUEST_PASSWORD_PROMPT
action.accountUuid,
action.accountName,
action.inputIncomingServerPassword,
action.incomingServerName,
action.inputOutgoingServerPassword,
action.outgoingServerName,
targetFragment = this,
requestCode = REQUEST_PASSWORD_PROMPT
)
dialogFragment.show(requireFragmentManager(), null)
}

View file

@ -46,12 +46,12 @@ class SettingsImportViewModel(
private val selectedAccounts: Set<AccountUuid>
get() {
return uiModel.settingsList.asSequence()
.filterIsInstance<SettingsListItem.Account>()
.filter { it.selected }
.map {
accountsMap[it.accountIndex] ?: error("Unknown account index: ${it.accountIndex}")
}
.toSet()
.filterIsInstance<SettingsListItem.Account>()
.filter { it.selected }
.map {
accountsMap[it.accountIndex] ?: error("Unknown account index: ${it.accountIndex}")
}
.toSet()
}
fun getActionEvents(): LiveData<Action> = actionLiveData
@ -83,8 +83,10 @@ class SettingsImportViewModel(
if (!hasDocumentBeenRead) return@updateUiModel
val includeGeneralSettings = savedInstanceState.getBoolean(STATE_INCLUDE_GENERAL_SETTINGS)
val generalSettingsImportState = savedInstanceState.getEnum(STATE_GENERAL_SETTINGS_IMPORT_STATUS,
ImportStatus.NOT_AVAILABLE)
val generalSettingsImportState = savedInstanceState.getEnum(
STATE_GENERAL_SETTINGS_IMPORT_STATUS,
ImportStatus.NOT_AVAILABLE
)
val generalSettingsItem = SettingsListItem.GeneralSettings().apply {
selected = includeGeneralSettings
@ -97,10 +99,10 @@ class SettingsImportViewModel(
savedAccountList.forEach { saved ->
accountsMap[saved.accountIndex] = saved.accountUuid
accountStateMap[saved.accountIndex] = AccountState(
saved.incomingServerName,
saved.outgoingServerName,
saved.incomingServerPasswordNeeded,
saved.outgoingServerPasswordNeeded
saved.incomingServerName,
saved.outgoingServerName,
saved.incomingServerPasswordNeeded,
saved.outgoingServerPasswordNeeded
)
}
@ -227,9 +229,9 @@ class SettingsImportViewModel(
}
accountsMap = contents.accounts
.asSequence()
.mapIndexed { index, account -> index to account.uuid }
.toMap(mutableMapOf())
.asSequence()
.mapIndexed { index, account -> index to account.uuid }
.toMap(mutableMapOf())
val items = mutableListOf<SettingsListItem>(SettingsListItem.GeneralSettings())
contents.accounts.mapIndexedTo(items) { index, accountDescription ->
@ -312,10 +314,10 @@ class SettingsImportViewModel(
if (accountPair.incomingPasswordNeeded || accountPair.outgoingPasswordNeeded) {
accountStateMap[accountIndex] = AccountState(
accountPair.incomingServerName,
accountPair.outgoingServerName,
accountPair.incomingPasswordNeeded,
accountPair.outgoingPasswordNeeded
accountPair.incomingServerName,
accountPair.outgoingServerName,
accountPair.incomingPasswordNeeded,
accountPair.outgoingPasswordNeeded
)
setSettingsListState(index, ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED)
@ -347,14 +349,16 @@ class SettingsImportViewModel(
val accountUuid = accountsMap[accountIndex]!!
val accountState = accountStateMap[accountIndex]!!
sendActionEvent(Action.PasswordPrompt(
sendActionEvent(
Action.PasswordPrompt(
accountUuid,
settingsListItem.displayName,
accountState.incomingServerPasswordNeeded,
accountState.incomingServerName,
accountState.outgoingServerPasswordNeeded,
accountState.outgoingServerName
))
)
)
}
private fun updateUiModel(block: SettingsImportUiModel.() -> Unit) {
@ -368,24 +372,24 @@ class SettingsImportViewModel(
private fun createSavedAccountList(): ArrayList<SavedAccountState> {
return uiModel.settingsList
.asSequence()
.filterIsInstance<SettingsListItem.Account>()
.map { accountItem ->
val accountIndex = accountItem.accountIndex
val accountState = accountStateMap[accountIndex]
SavedAccountState(
accountIndex = accountIndex,
displayName = accountItem.displayName,
accountUuid = accountsMap[accountIndex]!!,
selected = accountItem.selected,
importStatus = accountItem.importStatus,
incomingServerName = accountState?.incomingServerName,
outgoingServerName = accountState?.outgoingServerName,
incomingServerPasswordNeeded = accountState?.incomingServerPasswordNeeded ?: false,
outgoingServerPasswordNeeded = accountState?.outgoingServerPasswordNeeded ?: false
)
}
.toCollection(ArrayList(uiModel.settingsList.size - 1))
.asSequence()
.filterIsInstance<SettingsListItem.Account>()
.map { accountItem ->
val accountIndex = accountItem.accountIndex
val accountState = accountStateMap[accountIndex]
SavedAccountState(
accountIndex = accountIndex,
displayName = accountItem.displayName,
accountUuid = accountsMap[accountIndex]!!,
selected = accountItem.selected,
importStatus = accountItem.importStatus,
incomingServerName = accountState?.incomingServerName,
outgoingServerName = accountState?.outgoingServerName,
incomingServerPasswordNeeded = accountState?.incomingServerPasswordNeeded ?: false,
outgoingServerPasswordNeeded = accountState?.outgoingServerPasswordNeeded ?: false
)
}
.toCollection(ArrayList(uiModel.settingsList.size - 1))
}
companion object {

View file

@ -11,9 +11,9 @@ class WebViewConfigProvider(private val themeManager: ThemeManager) {
private fun createWebViewConfig(theme: Theme): WebViewConfig {
return WebViewConfig(
useDarkMode = theme == Theme.DARK,
autoFitWidth = K9.isAutoFitWidth,
textZoom = K9.fontSizes.messageViewContentAsPercent
useDarkMode = theme == Theme.DARK,
autoFitWidth = K9.isAutoFitWidth,
textZoom = K9.fontSizes.messageViewContentAsPercent
)
}
}

View file

@ -286,8 +286,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withoutSenderAboveSubjectAndDefaultFontSize_shouldNotSetTextSizeOfFirstLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(subject = FONT_DEFAULT),
senderAboveSubject = false
fontSizes = createFontSizes(subject = FONT_DEFAULT),
senderAboveSubject = false
)
val view = adapter.createAndBindView()
@ -298,8 +298,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withoutSenderAboveSubjectAndNonDefaultFontSize_shouldSetTextSizeOfFirstLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(subject = LARGE),
senderAboveSubject = false
fontSizes = createFontSizes(subject = LARGE),
senderAboveSubject = false
)
val view = adapter.createAndBindView()
@ -310,8 +310,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withSenderAboveSubjectAndDefaultFontSize_shouldNotSetTextSizeOfFirstLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(sender = FONT_DEFAULT),
senderAboveSubject = true
fontSizes = createFontSizes(sender = FONT_DEFAULT),
senderAboveSubject = true
)
val view = adapter.createAndBindView()
@ -322,8 +322,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withSenderAboveSubjectAndNonDefaultFontSize_shouldSetTextSizeOfFirstLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(sender = LARGE),
senderAboveSubject = true
fontSizes = createFontSizes(sender = LARGE),
senderAboveSubject = true
)
val view = adapter.createAndBindView()
@ -334,8 +334,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withoutSenderAboveSubjectAndDefaultFontSize_shouldNotSetTextSizeSpanInSecondLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(sender = FONT_DEFAULT),
senderAboveSubject = false
fontSizes = createFontSizes(sender = FONT_DEFAULT),
senderAboveSubject = false
)
val view = adapter.createAndBindView()
@ -346,8 +346,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withoutSenderAboveSubjectAndNonDefaultFontSize_shouldSetTextSizeSpanInSecondLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(sender = LARGE),
senderAboveSubject = false
fontSizes = createFontSizes(sender = LARGE),
senderAboveSubject = false
)
val view = adapter.createAndBindView()
@ -358,8 +358,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withSenderAboveSubjectAndDefaultFontSize_shouldNotSetTextSizeSpanInSecondLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(subject = FONT_DEFAULT),
senderAboveSubject = true
fontSizes = createFontSizes(subject = FONT_DEFAULT),
senderAboveSubject = true
)
val view = adapter.createAndBindView()
@ -370,8 +370,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun withSenderAboveSubjectAndNonDefaultFontSize_shouldSetTextSizeSpanInSecondLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(subject = LARGE),
senderAboveSubject = true
fontSizes = createFontSizes(subject = LARGE),
senderAboveSubject = true
)
val view = adapter.createAndBindView()
@ -400,8 +400,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun previewWithDefaultFontSize_shouldNotSetTextSizeOfSecondLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(preview = FONT_DEFAULT),
previewLines = 1
fontSizes = createFontSizes(preview = FONT_DEFAULT),
previewLines = 1
)
val view = adapter.createAndBindView()
@ -412,8 +412,8 @@ class MessageListAdapterTest : RobolectricTest() {
@Test
fun previewWithNonDefaultFontSize_shouldSetTextSizeOfSecondLineView() {
val adapter = createAdapter(
fontSizes = createFontSizes(preview = LARGE),
previewLines = 1
fontSizes = createFontSizes(preview = LARGE),
previewLines = 1
)
val view = adapter.createAndBindView()
@ -446,14 +446,14 @@ class MessageListAdapterTest : RobolectricTest() {
showAccountChip: Boolean = false
): MessageListAdapter {
val appearance = MessageListAppearance(
fontSizes,
previewLines,
stars,
senderAboveSubject,
showContactPicture,
showingThreadedList,
backGroundAsReadIndicator,
showAccountChip
fontSizes,
previewLines,
stars,
senderAboveSubject,
showContactPicture,
showingThreadedList,
backGroundAsReadIndicator,
showAccountChip
)
return MessageListAdapter(

View file

@ -63,15 +63,15 @@ import org.robolectric.annotation.LooperMode
class PgpMessageBuilderTest : K9RobolectricTest() {
private val defaultCryptoStatus = ComposeCryptoStatus(
OpenPgpProviderState.OK,
TEST_KEY_ID,
emptyList<RecipientSelectView.Recipient>(),
false,
false,
false,
true,
true,
CryptoMode.NO_CHOICE
OpenPgpProviderState.OK,
TEST_KEY_ID,
emptyList<RecipientSelectView.Recipient>(),
false,
false,
false,
true,
true,
CryptoMode.NO_CHOICE
)
private val resourceProvider: CoreResourceProvider by inject()
private val openPgpApi = mock(OpenPgpApi::class.java)
@ -83,7 +83,7 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
fun setUp() {
BinaryTempFileBody.setTempDirectory(RuntimeEnvironment.application.cacheDir)
`when`(autocryptOpenPgpApiInteractor.getKeyMaterialForKeyId(openPgpApi, TEST_KEY_ID, SENDER_EMAIL))
.thenReturn(AUTOCRYPT_KEY_MATERIAL)
.thenReturn(AUTOCRYPT_KEY_MATERIAL)
}
@Test
@ -189,8 +189,12 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
returnIntent.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, byteArrayOf(1, 2, 3))
`when`(openPgpApi.executeApi(capturedApiIntent.capture(),
any<OpenPgpDataSource>(), anyOrNull())).thenReturn(returnIntent)
`when`(
openPgpApi.executeApi(
capturedApiIntent.capture(),
any<OpenPgpDataSource>(), anyOrNull()
)
).thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -211,20 +215,28 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
Assert.assertEquals("multipart/signed must consist of two parts", 2, multipart.count.toLong())
val contentBodyPart = multipart.getBodyPart(0)
Assert.assertEquals("first part must have content type text/plain",
"text/plain", MimeUtility.getHeaderParameter(contentBodyPart.contentType, null))
Assert.assertEquals(
"first part must have content type text/plain",
"text/plain", MimeUtility.getHeaderParameter(contentBodyPart.contentType, null)
)
assertTrue("signed message body must be TextBody", contentBodyPart.body is TextBody)
Assert.assertEquals(MimeUtil.ENC_QUOTED_PRINTABLE, (contentBodyPart.body as TextBody).encoding)
assertContentOfBodyPartEquals("content must match the message text", contentBodyPart, TEST_MESSAGE_TEXT)
val signatureBodyPart = multipart.getBodyPart(1)
val contentType = signatureBodyPart.contentType
Assert.assertEquals("second part must be pgp signature", "application/pgp-signature",
MimeUtility.getHeaderParameter(contentType, null))
Assert.assertEquals("second part must be called signature.asc", "signature.asc",
MimeUtility.getHeaderParameter(contentType, "name"))
assertContentOfBodyPartEquals("content must match the supplied detached signature",
signatureBodyPart, byteArrayOf(1, 2, 3))
Assert.assertEquals(
"second part must be pgp signature", "application/pgp-signature",
MimeUtility.getHeaderParameter(contentType, null)
)
Assert.assertEquals(
"second part must be called signature.asc", "signature.asc",
MimeUtility.getHeaderParameter(contentType, "name")
)
assertContentOfBodyPartEquals(
"content must match the supplied detached signature",
signatureBodyPart, byteArrayOf(1, 2, 3)
)
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
}
@ -237,10 +249,10 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = mock(Intent::class.java)
`when`(returnIntent.getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt()))
.thenReturn(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED)
.thenReturn(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED)
val mockPendingIntent = mock(PendingIntent::class.java)
`when`<Parcelable>(returnIntent.getParcelableExtra<Parcelable>(eq(OpenPgpApi.RESULT_INTENT)))
.thenReturn(mockPendingIntent)
.thenReturn(mockPendingIntent)
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntent)
@ -268,7 +280,7 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val mockPendingIntent = mock(PendingIntent::class.java)
`when`<Parcelable>(returnIntent.getParcelableExtra<Parcelable>(eq(OpenPgpApi.RESULT_INTENT)))
.thenReturn(mockPendingIntent)
.thenReturn(mockPendingIntent)
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntent)
@ -308,7 +320,7 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = spy(Intent())
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
.thenReturn(returnIntent)
.thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -342,8 +354,8 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Throws(MessagingException::class)
fun buildDraft_replyToEncrypted() {
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.NO_CHOICE,
isReplyToEncrypted = true
cryptoMode = CryptoMode.NO_CHOICE,
isReplyToEncrypted = true
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
pgpMessageBuilder.isDraft = true
@ -381,7 +393,7 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = spy(Intent())
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
.thenReturn(returnIntent)
.thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -397,14 +409,16 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Test
@Throws(MessagingException::class)
fun buildEncrypt__checkGossip() {
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.CHOICE_ENABLED,
recipientAddresses = listOf("alice@example.org", "bob@example.org"))
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.CHOICE_ENABLED,
recipientAddresses = listOf("alice@example.org", "bob@example.org")
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
.thenReturn(returnIntent)
.thenReturn(returnIntent)
pgpMessageBuilder.buildAsync(mock(Callback::class.java))
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("alice@example.org"))
@ -415,15 +429,16 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Throws(MessagingException::class)
fun buildEncrypt__checkGossip__filterBcc() {
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.CHOICE_ENABLED,
recipientAddresses = listOf("alice@example.org", "bob@example.org", "carol@example.org"))
cryptoMode = CryptoMode.CHOICE_ENABLED,
recipientAddresses = listOf("alice@example.org", "bob@example.org", "carol@example.org")
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
pgpMessageBuilder.setBcc(listOf(Address("carol@example.org")))
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
.thenReturn(returnIntent)
.thenReturn(returnIntent)
pgpMessageBuilder.buildAsync(mock(Callback::class.java))
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForKeyId(same(openPgpApi), eq(TEST_KEY_ID), eq(SENDER_EMAIL))
@ -435,16 +450,17 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Throws(MessagingException::class)
fun buildEncrypt__checkGossip__filterBccSingleRecipient() {
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.CHOICE_ENABLED,
isPgpInlineModeEnabled = true,
recipientAddresses = listOf("alice@example.org", "carol@example.org"))
cryptoMode = CryptoMode.CHOICE_ENABLED,
isPgpInlineModeEnabled = true,
recipientAddresses = listOf("alice@example.org", "carol@example.org")
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
pgpMessageBuilder.setBcc(listOf(Address("carol@example.org")))
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
.thenReturn(returnIntent)
.thenReturn(returnIntent)
pgpMessageBuilder.buildAsync(mock(Callback::class.java))
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForKeyId(any(OpenPgpApi::class.java), any(Long::class.java), any(String::class.java))
@ -455,8 +471,9 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Throws(MessagingException::class)
fun buildEncrypt__shouldSucceed() {
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.CHOICE_ENABLED,
recipientAddresses = listOf("test@example.org"))
cryptoMode = CryptoMode.CHOICE_ENABLED,
recipientAddresses = listOf("test@example.org")
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
val capturedApiIntent = ArgumentCaptor.forClass(Intent::class.java)
@ -464,8 +481,12 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
any(OutputStream::class.java))).thenReturn(returnIntent)
`when`(
openPgpApi.executeApi(
capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
any(OutputStream::class.java)
)
).thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -489,16 +510,24 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
Assert.assertEquals("multipart/encrypted must consist of two parts", 2, multipart.count.toLong())
val dummyBodyPart = multipart.getBodyPart(0)
Assert.assertEquals("first part must be pgp encrypted dummy part",
"application/pgp-encrypted", dummyBodyPart.contentType)
assertContentOfBodyPartEquals("content must match the supplied detached signature",
dummyBodyPart, "Version: 1")
Assert.assertEquals(
"first part must be pgp encrypted dummy part",
"application/pgp-encrypted", dummyBodyPart.contentType
)
assertContentOfBodyPartEquals(
"content must match the supplied detached signature",
dummyBodyPart, "Version: 1"
)
val encryptedBodyPart = multipart.getBodyPart(1)
Assert.assertEquals("second part must be octet-stream of encrypted data",
"application/octet-stream; name=\"encrypted.asc\"", encryptedBodyPart.contentType)
assertTrue("message body must be BinaryTempFileBody",
encryptedBodyPart.body is BinaryTempFileBody)
Assert.assertEquals(
"second part must be octet-stream of encrypted data",
"application/octet-stream; name=\"encrypted.asc\"", encryptedBodyPart.contentType
)
assertTrue(
"message body must be BinaryTempFileBody",
encryptedBodyPart.body is BinaryTempFileBody
)
Assert.assertEquals(MimeUtil.ENC_7BIT, (encryptedBodyPart.body as BinaryTempFileBody).encoding)
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
@ -508,9 +537,10 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Throws(MessagingException::class)
fun buildEncrypt__withInlineEnabled__shouldSucceed() {
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.CHOICE_ENABLED,
isPgpInlineModeEnabled = true,
recipientAddresses = listOf("test@example.org"))
cryptoMode = CryptoMode.CHOICE_ENABLED,
isPgpInlineModeEnabled = true,
recipientAddresses = listOf("test@example.org")
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
val capturedApiIntent = ArgumentCaptor.forClass(Intent::class.java)
@ -518,8 +548,12 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
any(OutputStream::class.java))).thenReturn(returnIntent)
`when`(
openPgpApi.executeApi(
capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
any(OutputStream::class.java)
)
).thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -547,9 +581,10 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
@Throws(MessagingException::class)
fun buildSign__withInlineEnabled__shouldSucceed() {
val cryptoStatus = defaultCryptoStatus.copy(
cryptoMode = CryptoMode.SIGN_ONLY,
isPgpInlineModeEnabled = true,
recipientAddresses = listOf("test@example.org"))
cryptoMode = CryptoMode.SIGN_ONLY,
isPgpInlineModeEnabled = true,
recipientAddresses = listOf("test@example.org")
)
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
@ -558,8 +593,12 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
`when`(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
any(OutputStream::class.java))).thenReturn(returnIntent)
`when`(
openPgpApi.executeApi(
capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
any(OutputStream::class.java)
)
).thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -619,11 +658,13 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
val returnIntent = Intent()
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)
returnIntent.putExtra(OpenPgpApi.RESULT_ERROR,
OpenPgpError(OpenPgpError.OPPORTUNISTIC_MISSING_KEYS, "Missing keys"))
returnIntent.putExtra(
OpenPgpApi.RESULT_ERROR,
OpenPgpError(OpenPgpError.OPPORTUNISTIC_MISSING_KEYS, "Missing keys")
)
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
.thenReturn(returnIntent)
.thenReturn(returnIntent)
val mockCallback = mock(Callback::class.java)
pgpMessageBuilder.buildAsync(mockCallback)
@ -666,41 +707,42 @@ class PgpMessageBuilderTest : K9RobolectricTest() {
resourceProvider: CoreResourceProvider
): PgpMessageBuilder {
val builder = PgpMessageBuilder(
MessageIdGenerator.getInstance(), BoundaryGenerator.getInstance(),
AutocryptOperations.getInstance(), autocryptOpenPgpApiInteractor, resourceProvider)
MessageIdGenerator.getInstance(), BoundaryGenerator.getInstance(),
AutocryptOperations.getInstance(), autocryptOpenPgpApiInteractor, resourceProvider
)
builder.setOpenPgpApi(openPgpApi)
val identity = Identity(
name = "tester",
email = SENDER_EMAIL,
description = "test identity",
signatureUse = false
name = "tester",
email = SENDER_EMAIL,
description = "test identity",
signatureUse = false
)
builder.setSubject("subject")
.setSentDate(Date())
.setHideTimeZone(false)
.setTo(ArrayList())
.setCc(ArrayList())
.setBcc(ArrayList())
.setInReplyTo("inreplyto")
.setReferences("references")
.setRequestReadReceipt(false)
.setIdentity(identity)
.setMessageFormat(SimpleMessageFormat.TEXT)
.setText(TEST_MESSAGE_TEXT)
.setAttachments(ArrayList())
.setSignature("signature")
.setQuoteStyle(QuoteStyle.PREFIX)
.setQuotedTextMode(QuotedTextMode.NONE)
.setQuotedText("quoted text")
.setQuotedHtmlContent(InsertableHtmlContent())
.setReplyAfterQuote(false)
.setSignatureBeforeQuotedText(false)
.setIdentityChanged(false)
.setSignatureChanged(false)
.setCursorPosition(0)
.setMessageReference(null).isDraft = false
.setSentDate(Date())
.setHideTimeZone(false)
.setTo(ArrayList())
.setCc(ArrayList())
.setBcc(ArrayList())
.setInReplyTo("inreplyto")
.setReferences("references")
.setRequestReadReceipt(false)
.setIdentity(identity)
.setMessageFormat(SimpleMessageFormat.TEXT)
.setText(TEST_MESSAGE_TEXT)
.setAttachments(ArrayList())
.setSignature("signature")
.setQuoteStyle(QuoteStyle.PREFIX)
.setQuotedTextMode(QuotedTextMode.NONE)
.setQuotedText("quoted text")
.setQuotedHtmlContent(InsertableHtmlContent())
.setReplyAfterQuote(false)
.setSignatureBeforeQuotedText(false)
.setIdentityChanged(false)
.setSignatureChanged(false)
.setCursorPosition(0)
.setMessageReference(null).isDraft = false
return builder
}

View file

@ -24,7 +24,8 @@ internal class CommandFetchMessage(private val imapStore: ImapStore) {
// fun fact: ImapFolder.fetch can't handle getting STRUCTURE at same time as headers
if (fetchProfile.contains(FetchProfile.Item.STRUCTURE) &&
fetchProfile.contains(FetchProfile.Item.ENVELOPE)) {
fetchProfile.contains(FetchProfile.Item.ENVELOPE)
) {
val headerFetchProfile = fetchProfile.without(FetchProfile.Item.STRUCTURE)
val structureFetchProfile = FetchProfile().apply { add(FetchProfile.Item.STRUCTURE) }

View file

@ -15,8 +15,8 @@ internal class CommandSearch(private val imapStore: ImapStore) {
val folder = imapStore.getFolder(folderServerId)
try {
return folder.search(query, requiredFlags, forbiddenFlags, performFullTextSearch)
.sortedWith(UidReverseComparator())
.map { it.uid }
.sortedWith(UidReverseComparator())
.map { it.uid }
} finally {
folder.close()
}

View file

@ -486,7 +486,8 @@ internal class ImapSync(
}
if (syncConfig.maximumAutoDownloadMessageSize > 0 &&
message.size > syncConfig.maximumAutoDownloadMessageSize) {
message.size > syncConfig.maximumAutoDownloadMessageSize
) {
largeMessages.add(message)
} else {
smallMessages.add(message)

View file

@ -6,7 +6,6 @@ import com.fsck.k9.backend.api.FolderInfo
import com.fsck.k9.mail.AuthenticationFailedException
import com.fsck.k9.mail.FolderType
import com.fsck.k9.mail.MessagingException
import java.lang.Exception
import rs.ltt.jmap.client.JmapClient
import rs.ltt.jmap.client.api.ErrorResponseException
import rs.ltt.jmap.client.api.InvalidSessionResourceException

View file

@ -59,17 +59,18 @@ class CommandSetFlag(
Timber.v("Trying to mark up to %d messages in %s as read", limit, folderServerId)
val queryEmailCall = jmapClient.call(
QueryEmailMethodCall.builder()
.accountId(accountId)
.filter(EmailFilterCondition.builder()
.inMailbox(folderServerId)
.notKeyword("\$seen")
.build()
)
.calculateTotal(true)
.limit(limit)
QueryEmailMethodCall.builder()
.accountId(accountId)
.filter(
EmailFilterCondition.builder()
.inMailbox(folderServerId)
.notKeyword("\$seen")
.build()
)
.calculateTotal(true)
.limit(limit)
.build()
)
val queryEmailResponse = queryEmailCall.getMainResponseBlocking<QueryEmailMethodResponse>()
val numberOfReturnedEmails = queryEmailResponse.ids.size

Some files were not shown because too many files have changed in this diff Show more