Format Kotlin code
This commit is contained in:
parent
10be88d3e1
commit
1873593dc5
112 changed files with 1387 additions and 991 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@file:JvmName("Preconditions")
|
||||
|
||||
package com.fsck.k9.controller
|
||||
|
||||
import com.fsck.k9.K9
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@file:JvmName("StringHelper")
|
||||
|
||||
package com.fsck.k9.helper
|
||||
|
||||
fun isNullOrEmpty(text: String?) = text.isNullOrEmpty()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@file:JvmName("FolderTypeConverter")
|
||||
|
||||
package com.fsck.k9.mailstore
|
||||
|
||||
import com.fsck.k9.mail.FolderType
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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;\">")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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()) }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@file:JvmName("LocalSearchExtensions")
|
||||
|
||||
package com.fsck.k9.search
|
||||
|
||||
import com.fsck.k9.Account
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" +
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -29,7 +29,7 @@ class App : Application() {
|
|||
|
||||
companion object {
|
||||
val appConfig = AppConfig(
|
||||
componentsToDisable = listOf(MessageCompose::class.java)
|
||||
componentsToDisable = listOf(MessageCompose::class.java)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ private val mainAppModule = module {
|
|||
}
|
||||
|
||||
val appModules = listOf(
|
||||
mainAppModule,
|
||||
notificationModule,
|
||||
resourcesModule,
|
||||
backendsModule,
|
||||
storageModule,
|
||||
uiAddAccountModule
|
||||
mainAppModule,
|
||||
notificationModule,
|
||||
resourcesModule,
|
||||
backendsModule,
|
||||
storageModule,
|
||||
uiAddAccountModule
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) }
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -31,7 +31,7 @@ class App : Application() {
|
|||
|
||||
companion object {
|
||||
val appConfig = AppConfig(
|
||||
componentsToDisable = listOf(MessageCompose::class.java)
|
||||
componentsToDisable = listOf(MessageCompose::class.java)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) }
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()) }
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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!!)
|
||||
|
||||
|
|
|
@ -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()) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue