uidMap = null;
Long trashFolderId = account.getTrashFolderId();
+ boolean isSpamFolder = account.hasSpamFolder() && account.getSpamFolderId() == folderId;
+
LocalFolder localTrashFolder = null;
- if (!account.hasTrashFolder() || folderId == trashFolderId ||
+ if (skipTrashFolder || !account.hasTrashFolder() || folderId == trashFolderId || isSpamFolder ||
(backend.getSupportsTrashFolder() && !backend.isDeleteMoveToTrash())) {
Timber.d("Not moving deleted messages to local Trash folder. Removing local copies.");
diff --git a/app/core/src/main/java/com/fsck/k9/helper/Utility.java b/app/core/src/main/java/com/fsck/k9/helper/Utility.java
index 1788f8b3a..5671f68bb 100644
--- a/app/core/src/main/java/com/fsck/k9/helper/Utility.java
+++ b/app/core/src/main/java/com/fsck/k9/helper/Utility.java
@@ -121,7 +121,7 @@ public class Utility {
/**
* Extract the 'original' subject value, by ignoring leading
* response/forward marker and '[XX]' formatted tags (as many mailing-list
- * softwares do).
+ * software do).
*
*
* Result is also trimmed.
diff --git a/app/core/src/main/java/com/fsck/k9/job/K9WorkerFactory.kt b/app/core/src/main/java/com/fsck/k9/job/K9WorkerFactory.kt
index adcb92588..9251fb486 100644
--- a/app/core/src/main/java/com/fsck/k9/job/K9WorkerFactory.kt
+++ b/app/core/src/main/java/com/fsck/k9/job/K9WorkerFactory.kt
@@ -4,23 +4,16 @@ import android.content.Context
import androidx.work.ListenableWorker
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
-import com.fsck.k9.Preferences
-import com.fsck.k9.controller.MessagingController
+import org.koin.core.parameter.parametersOf
+import org.koin.java.KoinJavaComponent.getKoin
-class K9WorkerFactory(
- private val messagingController: MessagingController,
- private val preferences: Preferences
-) : WorkerFactory() {
+class K9WorkerFactory : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
- return when (workerClassName) {
- MailSyncWorker::class.java.canonicalName -> {
- MailSyncWorker(messagingController, preferences, appContext, workerParameters)
- }
- else -> null
- }
+ val workerClass = Class.forName(workerClassName).kotlin
+ return getKoin().getOrNull(workerClass) { parametersOf(workerParameters) }
}
}
diff --git a/app/core/src/main/java/com/fsck/k9/job/KoinModule.kt b/app/core/src/main/java/com/fsck/k9/job/KoinModule.kt
index 4dd6f0e8f..8158ae494 100644
--- a/app/core/src/main/java/com/fsck/k9/job/KoinModule.kt
+++ b/app/core/src/main/java/com/fsck/k9/job/KoinModule.kt
@@ -1,12 +1,18 @@
package com.fsck.k9.job
+import android.content.Context
+import androidx.work.WorkManager
import androidx.work.WorkerFactory
+import androidx.work.WorkerParameters
import org.koin.dsl.module
val jobModule = module {
- single { WorkManagerProvider(get(), get()) }
- single { K9WorkerFactory(get(), get()) }
- single { get().getWorkManager() }
- single { K9JobManager(get(), get(), get()) }
+ single { WorkManagerConfigurationProvider(workerFactory = get()) }
+ single { K9WorkerFactory() }
+ single { WorkManager.getInstance(get()) }
+ single { K9JobManager(workManager = get(), preferences = get(), mailSyncWorkerManager = get()) }
factory { MailSyncWorkerManager(workManager = get(), clock = get()) }
+ factory { (parameters: WorkerParameters) ->
+ MailSyncWorker(messagingController = get(), preferences = get(), context = get(), parameters)
+ }
}
diff --git a/app/core/src/main/java/com/fsck/k9/job/WorkManagerConfigurationProvider.kt b/app/core/src/main/java/com/fsck/k9/job/WorkManagerConfigurationProvider.kt
new file mode 100644
index 000000000..54d973fee
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/job/WorkManagerConfigurationProvider.kt
@@ -0,0 +1,12 @@
+package com.fsck.k9.job
+
+import androidx.work.Configuration
+import androidx.work.WorkerFactory
+
+class WorkManagerConfigurationProvider(private val workerFactory: WorkerFactory) {
+ fun getConfiguration(): Configuration {
+ return Configuration.Builder()
+ .setWorkerFactory(workerFactory)
+ .build()
+ }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/job/WorkManagerProvider.kt b/app/core/src/main/java/com/fsck/k9/job/WorkManagerProvider.kt
deleted file mode 100644
index f31fbaaf7..000000000
--- a/app/core/src/main/java/com/fsck/k9/job/WorkManagerProvider.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.fsck.k9.job
-
-import android.content.Context
-import androidx.work.Configuration
-import androidx.work.WorkManager
-import androidx.work.WorkerFactory
-
-class WorkManagerProvider(private val context: Context, private val workerFactory: WorkerFactory) {
- fun getWorkManager(): WorkManager {
- val configuration = Configuration.Builder()
- .setWorkerFactory(workerFactory)
- .build()
-
- WorkManager.initialize(context, configuration)
-
- return WorkManager.getInstance(context)
- }
-}
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java b/app/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java
index 3f846a6c6..01af19471 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java
@@ -651,9 +651,13 @@ public class LocalStore {
if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(encoding)) {
return new QuotedPrintableInputStream(rawInputStream) {
@Override
- public void close() throws IOException {
+ public void close() {
super.close();
- rawInputStream.close();
+ try {
+ rawInputStream.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
};
}
diff --git a/app/core/src/main/java/com/fsck/k9/message/extractors/TextPartFinder.java b/app/core/src/main/java/com/fsck/k9/message/extractors/TextPartFinder.java
deleted file mode 100644
index 2168da54f..000000000
--- a/app/core/src/main/java/com/fsck/k9/message/extractors/TextPartFinder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.fsck.k9.message.extractors;
-
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.fsck.k9.mail.Body;
-import com.fsck.k9.mail.BodyPart;
-import com.fsck.k9.mail.Multipart;
-import com.fsck.k9.mail.Part;
-
-import static com.fsck.k9.mail.internet.MimeUtility.isSameMimeType;
-
-
-public class TextPartFinder {
- @Nullable
- public Part findFirstTextPart(@NonNull Part part) {
- String mimeType = part.getMimeType();
- Body body = part.getBody();
-
- if (body instanceof Multipart) {
- Multipart multipart = (Multipart) body;
- if (isSameMimeType(mimeType, "multipart/alternative")) {
- return findTextPartInMultipartAlternative(multipart);
- } else {
- return findTextPartInMultipart(multipart);
- }
- } else if (isSameMimeType(mimeType, "text/plain") || isSameMimeType(mimeType, "text/html")) {
- return part;
- }
-
- return null;
- }
-
- private Part findTextPartInMultipartAlternative(Multipart multipart) {
- Part htmlPart = null;
-
- for (BodyPart bodyPart : multipart.getBodyParts()) {
- String mimeType = bodyPart.getMimeType();
- Body body = bodyPart.getBody();
-
- if (body instanceof Multipart) {
- Part candidatePart = findFirstTextPart(bodyPart);
- if (candidatePart != null) {
- if (isSameMimeType(candidatePart.getMimeType(), "text/html")) {
- htmlPart = candidatePart;
- } else {
- return candidatePart;
- }
- }
- } else if (isSameMimeType(mimeType, "text/plain")) {
- return bodyPart;
- } else if (isSameMimeType(mimeType, "text/html") && htmlPart == null) {
- htmlPart = bodyPart;
- }
- }
-
- if (htmlPart != null) {
- return htmlPart;
- }
-
- return null;
- }
-
- private Part findTextPartInMultipart(Multipart multipart) {
- for (BodyPart bodyPart : multipart.getBodyParts()) {
- String mimeType = bodyPart.getMimeType();
- Body body = bodyPart.getBody();
-
- if (body instanceof Multipart) {
- Part candidatePart = findFirstTextPart(bodyPart);
- if (candidatePart != null) {
- return candidatePart;
- }
- } else if (isSameMimeType(mimeType, "text/plain") || isSameMimeType(mimeType, "text/html")) {
- return bodyPart;
- }
- }
-
- return null;
- }
-}
diff --git a/app/core/src/main/java/com/fsck/k9/message/extractors/TextPartFinder.kt b/app/core/src/main/java/com/fsck/k9/message/extractors/TextPartFinder.kt
new file mode 100644
index 000000000..cca3d79c2
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/message/extractors/TextPartFinder.kt
@@ -0,0 +1,68 @@
+package com.fsck.k9.message.extractors
+
+import com.fsck.k9.mail.MimeType.Companion.toMimeType
+import com.fsck.k9.mail.MimeType.Companion.toMimeTypeOrNull
+import com.fsck.k9.mail.Multipart
+import com.fsck.k9.mail.Part
+
+private val TEXT_PLAIN = "text/plain".toMimeType()
+private val TEXT_HTML = "text/html".toMimeType()
+private val MULTIPART_ALTERNATIVE = "multipart/alternative".toMimeType()
+
+class TextPartFinder {
+ fun findFirstTextPart(part: Part): Part? {
+ val mimeType = part.mimeType.toMimeTypeOrNull()
+ val body = part.body
+
+ return if (body is Multipart) {
+ if (mimeType == MULTIPART_ALTERNATIVE) {
+ findTextPartInMultipartAlternative(body)
+ } else {
+ findTextPartInMultipart(body)
+ }
+ } else if (mimeType == TEXT_PLAIN || mimeType == TEXT_HTML) {
+ part
+ } else {
+ null
+ }
+ }
+
+ private fun findTextPartInMultipartAlternative(multipart: Multipart): Part? {
+ var htmlPart: Part? = null
+
+ for (bodyPart in multipart.bodyParts) {
+ val mimeType = bodyPart.mimeType.toMimeTypeOrNull()
+ val body = bodyPart.body
+
+ if (body is Multipart) {
+ val candidatePart = findFirstTextPart(bodyPart) ?: continue
+ if (mimeType == TEXT_PLAIN) {
+ return candidatePart
+ }
+
+ htmlPart = candidatePart
+ } else if (mimeType == TEXT_PLAIN) {
+ return bodyPart
+ } else if (mimeType == TEXT_HTML && htmlPart == null) {
+ htmlPart = bodyPart
+ }
+ }
+
+ return htmlPart
+ }
+
+ private fun findTextPartInMultipart(multipart: Multipart): Part? {
+ for (bodyPart in multipart.bodyParts) {
+ val mimeType = bodyPart.mimeType.toMimeTypeOrNull()
+ val body = bodyPart.body
+
+ if (body is Multipart) {
+ return findFirstTextPart(bodyPart) ?: continue
+ } else if (mimeType == TEXT_PLAIN || mimeType == TEXT_HTML) {
+ return bodyPart
+ }
+ }
+
+ return null
+ }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/message/html/BitcoinUriParser.java b/app/core/src/main/java/com/fsck/k9/message/html/BitcoinUriParser.java
deleted file mode 100644
index 632231a00..000000000
--- a/app/core/src/main/java/com/fsck/k9/message/html/BitcoinUriParser.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.fsck.k9.message.html;
-
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-
-class BitcoinUriParser implements UriParser {
- private static final Pattern BITCOIN_URI_PATTERN =
- Pattern.compile("bitcoin:[1-9a-km-zA-HJ-NP-Z]{27,34}(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?");
-
- @Nullable
- @Override
- public UriMatch parseUri(@NotNull CharSequence text, int startPos) {
- Matcher matcher = BITCOIN_URI_PATTERN.matcher(text);
-
- if (!matcher.find(startPos) || matcher.start() != startPos) {
- return null;
- }
-
- int startIndex = matcher.start();
- int endIndex = matcher.end();
- CharSequence uri = text.subSequence(startIndex, endIndex);
- return new UriMatch(startIndex, endIndex, uri);
- }
-}
diff --git a/app/core/src/main/java/com/fsck/k9/message/html/EthereumUriParser.java b/app/core/src/main/java/com/fsck/k9/message/html/EthereumUriParser.java
deleted file mode 100644
index 70f5878a2..000000000
--- a/app/core/src/main/java/com/fsck/k9/message/html/EthereumUriParser.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.fsck.k9.message.html;
-
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-
-/**
- * Parses ERC-67 URIs
- * https://github.com/ethereum/EIPs/issues/67
- */
-class EthereumUriParser implements UriParser {
- private static final Pattern ETHEREUM_URI_PATTERN =
- Pattern.compile("ethereum:0x[0-9a-fA-F]*(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?");
-
- @Nullable
- @Override
- public UriMatch parseUri(@NotNull CharSequence text, int startPos) {
- Matcher matcher = ETHEREUM_URI_PATTERN.matcher(text);
-
- if (!matcher.find(startPos) || matcher.start() != startPos) {
- return null;
- }
-
- int startIndex = matcher.start();
- int endIndex = matcher.end();
- CharSequence uri = text.subSequence(startIndex, endIndex);
- return new UriMatch(startIndex, endIndex, uri);
- }
-}
diff --git a/app/core/src/main/java/com/fsck/k9/message/html/GenericUriParser.kt b/app/core/src/main/java/com/fsck/k9/message/html/GenericUriParser.kt
new file mode 100644
index 000000000..14cede4cd
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/message/html/GenericUriParser.kt
@@ -0,0 +1,34 @@
+package com.fsck.k9.message.html
+
+import java.util.regex.Pattern
+
+/**
+ * Matches the URI generic syntax.
+ *
+ * See [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986).
+ */
+class GenericUriParser : UriParser {
+ override fun parseUri(text: CharSequence, startPos: Int): UriMatch? {
+ val matcher = PATTERN.matcher(text)
+ if (!matcher.find(startPos) || matcher.start() != startPos) return null
+
+ val startIndex = matcher.start()
+ val endIndex = matcher.end()
+ val uri = text.subSequence(startIndex, endIndex)
+
+ return UriMatch(startIndex, endIndex, uri)
+ }
+
+ companion object {
+ private const val SCHEME = "[a-zA-Z][a-zA-Z0-9+.\\-]*"
+ private const val AUTHORITY = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:\\[\\]@]*"
+ private const val PATH = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:@/]*"
+ private const val QUERY = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:@/?]*"
+ private const val FRAGMENT = "[a-zA-Z0-9\\-._~%!\$&'()*+,;=:@/?]*"
+
+ // This regular expression matches more than allowed by the generic URI syntax. So we might end up linkifying
+ // text that is not a proper URI. We leave apps actually handling the URI when the user clicks on such a link
+ // to deal with this case.
+ private val PATTERN = Pattern.compile("$SCHEME:(?://$AUTHORITY)?(?:$PATH)?(?:\\?$QUERY)?(?:#$FRAGMENT)?")
+ }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/message/html/UriMatcher.kt b/app/core/src/main/java/com/fsck/k9/message/html/UriMatcher.kt
index 5dea02560..44329acfd 100644
--- a/app/core/src/main/java/com/fsck/k9/message/html/UriMatcher.kt
+++ b/app/core/src/main/java/com/fsck/k9/message/html/UriMatcher.kt
@@ -3,12 +3,14 @@ package com.fsck.k9.message.html
object UriMatcher {
private val SUPPORTED_URIS = run {
val httpUriParser = HttpUriParser()
+ val genericUriParser = GenericUriParser()
mapOf(
- "ethereum:" to EthereumUriParser(),
- "bitcoin:" to BitcoinUriParser(),
"http:" to httpUriParser,
"https:" to httpUriParser,
- "rtsp:" to httpUriParser
+ "mailto:" to genericUriParser,
+ "matrix:" to genericUriParser,
+ "rtsp:" to httpUriParser,
+ "xmpp:" to genericUriParser,
)
}
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java b/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java
index af8a22247..cb4bfeb8e 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/GeneralSettingsDescriptions.java
@@ -271,6 +271,9 @@ public class GeneralSettingsDescriptions {
s.put("swipeLeftAction", Settings.versions(
new V(83, new EnumSetting<>(SwipeAction.class, SwipeAction.ToggleRead))
));
+ s.put("showComposeButtonOnMessageList", Settings.versions(
+ new V(85, new BooleanSetting(true))
+ ));
SETTINGS = Collections.unmodifiableMap(s);
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
index 85ffdd1ca..7f1274952 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
@@ -36,7 +36,7 @@ public class Settings {
*
* @see SettingsExporter
*/
- public static final int VERSION = 84;
+ public static final int VERSION = 85;
static Map validate(int version, Map> settings,
Map importedSettings, boolean useDefaultValues) {
diff --git a/app/core/src/main/java/com/fsck/k9/search/LocalSearch.java b/app/core/src/main/java/com/fsck/k9/search/LocalSearch.java
index 52f23ef4a..e538a9723 100644
--- a/app/core/src/main/java/com/fsck/k9/search/LocalSearch.java
+++ b/app/core/src/main/java/com/fsck/k9/search/LocalSearch.java
@@ -241,7 +241,7 @@ public class LocalSearch implements SearchSpecification {
}
///////////////////////////////////////////////////////////////
- // Public accesor methods
+ // Public accessor methods
///////////////////////////////////////////////////////////////
/**
* TODO THIS HAS TO GO!!!!
diff --git a/app/core/src/test/java/com/fsck/k9/TestApp.kt b/app/core/src/test/java/com/fsck/k9/TestApp.kt
index b365bd4dd..7f6f0f886 100644
--- a/app/core/src/test/java/com/fsck/k9/TestApp.kt
+++ b/app/core/src/test/java/com/fsck/k9/TestApp.kt
@@ -1,6 +1,7 @@
package com.fsck.k9
import android.app.Application
+import androidx.work.WorkManager
import com.fsck.k9.backend.BackendManager
import com.fsck.k9.controller.ControllerExtension
import com.fsck.k9.crypto.EncryptionExtractor
@@ -36,4 +37,5 @@ val testModule = module {
single { mock() }
single { mock() }
single(named("controllerExtensions")) { emptyList() }
+ single { mock() }
}
diff --git a/app/core/src/test/java/com/fsck/k9/message/extractors/TextPartFinderTest.java b/app/core/src/test/java/com/fsck/k9/message/extractors/TextPartFinderTest.java
deleted file mode 100644
index 30f6b2d19..000000000
--- a/app/core/src/test/java/com/fsck/k9/message/extractors/TextPartFinderTest.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package com.fsck.k9.message.extractors;
-
-
-import com.fsck.k9.mail.BodyPart;
-import com.fsck.k9.mail.Part;
-import org.junit.Before;
-import org.junit.Test;
-
-import static com.fsck.k9.message.MessageCreationHelper.createEmptyPart;
-import static com.fsck.k9.message.MessageCreationHelper.createMultipart;
-import static com.fsck.k9.message.MessageCreationHelper.createPart;
-import static com.fsck.k9.message.MessageCreationHelper.createTextPart;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-
-public class TextPartFinderTest {
- private TextPartFinder textPartFinder;
-
-
- @Before
- public void setUp() throws Exception {
- textPartFinder = new TextPartFinder();
- }
-
- @Test
- public void findFirstTextPart_withTextPlainPart() throws Exception {
- Part part = createTextPart("text/plain");
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(part, result);
- }
-
- @Test
- public void findFirstTextPart_withTextHtmlPart() throws Exception {
- Part part = createTextPart("text/html");
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(part, result);
- }
-
- @Test
- public void findFirstTextPart_withoutTextPart() throws Exception {
- Part part = createPart("image/jpeg");
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertNull(result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternative() throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/alternative", expected, createTextPart("text/html"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternativeHtmlPartFirst() throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/alternative", createTextPart("text/html"), expected);
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternativeContainingOnlyTextHtmlPart() throws Exception {
- BodyPart expected = createTextPart("text/html");
- Part part = createMultipart("multipart/alternative",
- createPart("image/gif"),
- expected,
- createTextPart("text/html"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternativeNotContainingTextPart() throws Exception {
- Part part = createMultipart("multipart/alternative",
- createPart("image/gif"),
- createPart("application/pdf"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertNull(result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternativeContainingMultipartRelatedContainingTextPlain()
- throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/alternative",
- createMultipart("multipart/related", expected, createPart("image/jpeg")),
- createTextPart("text/html"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternativeContainingMultipartRelatedContainingTextHtmlFirst()
- throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/alternative",
- createMultipart("multipart/related", createTextPart("text/html"), createPart("image/jpeg")),
- expected);
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartMixedContainingTextPlain() throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/mixed", createPart("image/jpeg"), expected);
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartMixedContainingTextHtmlFirst() throws Exception {
- BodyPart expected = createTextPart("text/html");
- Part part = createMultipart("multipart/mixed", expected, createTextPart("text/plain"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartMixedNotContainingTextPart() throws Exception {
- Part part = createMultipart("multipart/mixed", createPart("image/jpeg"), createPart("image/gif"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertNull(result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartMixedContainingMultipartAlternative() throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/mixed",
- createPart("image/jpeg"),
- createMultipart("multipart/alternative", expected, createTextPart("text/html")),
- createTextPart("text/plain"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartMixedContainingMultipartAlternativeWithTextPlainPartLast()
- throws Exception {
- BodyPart expected = createTextPart("text/plain");
- Part part = createMultipart("multipart/mixed",
- createMultipart("multipart/alternative", createTextPart("text/html"), expected));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartAlternativeContainingEmptyTextPlainPart()
- throws Exception {
- BodyPart expected = createEmptyPart("text/plain");
- Part part = createMultipart("multipart/alternative", expected, createTextPart("text/html"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-
- @Test
- public void findFirstTextPart_withMultipartMixedContainingEmptyTextHtmlPart()
- throws Exception {
- BodyPart expected = createEmptyPart("text/html");
- Part part = createMultipart("multipart/mixed", expected, createTextPart("text/plain"));
-
- Part result = textPartFinder.findFirstTextPart(part);
-
- assertEquals(expected, result);
- }
-}
diff --git a/app/core/src/test/java/com/fsck/k9/message/extractors/TextPartFinderTest.kt b/app/core/src/test/java/com/fsck/k9/message/extractors/TextPartFinderTest.kt
new file mode 100644
index 000000000..4f7f32c09
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/message/extractors/TextPartFinderTest.kt
@@ -0,0 +1,254 @@
+package com.fsck.k9.message.extractors
+
+import com.fsck.k9.message.MessageCreationHelper.createEmptyPart
+import com.fsck.k9.message.MessageCreationHelper.createMultipart
+import com.fsck.k9.message.MessageCreationHelper.createPart
+import com.fsck.k9.message.MessageCreationHelper.createTextPart
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class TextPartFinderTest {
+ private val textPartFinder = TextPartFinder()
+
+ @Test
+ fun `text_plain part`() {
+ val part = createTextPart("text/plain")
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(part)
+ }
+
+ @Test
+ fun `text_html part`() {
+ val part = createTextPart("text/html")
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(part)
+ }
+
+ @Test
+ fun `without text part`() {
+ val part = createPart("image/jpeg")
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `multipart_alternative text_plain and text_html`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/alternative",
+ expected,
+ createTextPart("text/html")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_alternative containing text_html and text_plain`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/alternative",
+ createTextPart("text/html"),
+ expected
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_alternative containing multiple text_html parts`() {
+ val expected = createTextPart("text/html")
+ val part = createMultipart(
+ "multipart/alternative",
+ createPart("image/gif"),
+ expected,
+ createTextPart("text/html")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_alternative not containing any text parts`() {
+ val part = createMultipart(
+ "multipart/alternative",
+ createPart("image/gif"),
+ createPart("application/pdf")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `multipart_alternative containing multipart_related containing text_plain`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/alternative",
+ createMultipart(
+ "multipart/related",
+ expected,
+ createPart("image/jpeg")
+ ),
+ createTextPart("text/html")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_alternative containing multipart_related and text_plain`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/alternative",
+ createMultipart(
+ "multipart/related",
+ createTextPart("text/html"),
+ createPart("image/jpeg")
+ ),
+ expected
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_mixed containing text_plain`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/mixed",
+ createPart("image/jpeg"),
+ expected
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_mixed containing text_html and text_plain`() {
+ val expected = createTextPart("text/html")
+ val part = createMultipart(
+ "multipart/mixed",
+ expected,
+ createTextPart("text/plain")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_mixed not containing any text parts`() {
+ val part = createMultipart(
+ "multipart/mixed",
+ createPart("image/jpeg"),
+ createPart("image/gif")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `multipart_mixed containing multipart_alternative containing text_plain and text_html`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/mixed",
+ createPart("image/jpeg"),
+ createMultipart(
+ "multipart/alternative",
+ expected,
+ createTextPart("text/html")
+ ),
+ createTextPart("text/plain")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_mixed containing multipart_alternative containing text_html and text_plain`() {
+ val expected = createTextPart("text/plain")
+ val part = createMultipart(
+ "multipart/mixed",
+ createMultipart(
+ "multipart/alternative",
+ createTextPart("text/html"),
+ expected
+ )
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_alternative containing empty text_plain and text_html`() {
+ val expected = createEmptyPart("text/plain")
+ val part = createMultipart(
+ "multipart/alternative",
+ expected,
+ createTextPart("text/html")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_mixed containing empty text_html and text_plain`() {
+ val expected = createEmptyPart("text/html")
+ val part = createMultipart(
+ "multipart/mixed",
+ expected,
+ createTextPart("text/plain")
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+
+ @Test
+ fun `multipart_mixed containing multipart_alternative and text_plain`() {
+ val expected = createEmptyPart("text/plain")
+ val part = createMultipart(
+ "multipart/mixed",
+ createMultipart(
+ "multipart/alternative",
+ createPart("image/jpeg"),
+ createPart("image/png")
+ ),
+ expected
+ )
+
+ val result = textPartFinder.findFirstTextPart(part)
+
+ assertThat(result).isEqualTo(expected)
+ }
+}
diff --git a/app/core/src/test/java/com/fsck/k9/message/html/BitcoinUriParserTest.java b/app/core/src/test/java/com/fsck/k9/message/html/BitcoinUriParserTest.java
deleted file mode 100644
index 0478024ce..000000000
--- a/app/core/src/test/java/com/fsck/k9/message/html/BitcoinUriParserTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.fsck.k9.message.html;
-
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-
-public class BitcoinUriParserTest {
- BitcoinUriParser parser = new BitcoinUriParser();
-
-
- @Test
- public void basicBitcoinUri() throws Exception {
- assertValidUri("bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU");
- }
-
- @Test
- public void bitcoinUriWithAmount() throws Exception {
- assertValidUri("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2");
- }
-
- @Test
- public void bitcoinUriWithQueryParameters() throws Exception {
- assertValidUri("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2" +
- "&message=Payment&label=Satoshi&extra=other-param");
- }
-
- @Test
- public void uriInMiddleOfInput() throws Exception {
- String prefix = "prefix ";
- String uri = "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2";
- String text = prefix + uri;
-
- UriMatch uriMatch = parser.parseUri(text, prefix.length());
-
- assertUriMatch(uri, uriMatch, prefix.length());
- }
-
- @Test
- public void invalidScheme() throws Exception {
- assertInvalidUri("bitcion:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU");
- }
-
- @Test
- public void invalidAddress() throws Exception {
- assertInvalidUri("bitcoin:[invalid]");
- }
-
-
- private void assertValidUri(String uri) {
- UriMatch uriMatch = parser.parseUri(uri, 0);
- assertUriMatch(uri, uriMatch);
- }
-
- private void assertUriMatch(String uri, UriMatch uriMatch) {
- assertUriMatch(uri, uriMatch, 0);
- }
-
- private void assertUriMatch(String uri, UriMatch uriMatch, int offset) {
- assertNotNull(uriMatch);
- assertEquals(offset, uriMatch.getStartIndex());
- assertEquals(uri.length() + offset, uriMatch.getEndIndex());
- assertEquals(uri, uriMatch.getUri().toString());
- }
-
- private void assertInvalidUri(String text) {
- UriMatch uriMatch = parser.parseUri(text, 0);
- assertNull(uriMatch);
- }
-}
diff --git a/app/core/src/test/java/com/fsck/k9/message/html/EthereumUriParserTest.java b/app/core/src/test/java/com/fsck/k9/message/html/EthereumUriParserTest.java
deleted file mode 100644
index 9927f329c..000000000
--- a/app/core/src/test/java/com/fsck/k9/message/html/EthereumUriParserTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.fsck.k9.message.html;
-
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-
-public class EthereumUriParserTest {
- EthereumUriParser parser = new EthereumUriParser();
-
-
- @Test
- public void basicEthereumUri() throws Exception {
- assertValidUri("ethereum:0xfdf1210fc262c73d0436236a0e07be419babbbc4");
- }
-
- @Test
- public void ethereumUriWithValue() throws Exception {
- assertValidUri("ethereum:0xfdf1210fc262c73d0436236a0e07be419babbbc4?value=42");
- }
-
- @Test
- public void ethereumUriWithQueryParameters() throws Exception {
- assertValidUri("ethereum:0xfdf1210fc262c73d0436236a0e07be419babbbc4?value=42" +
- "&gas=100000&bytecode=0xa9059cbb0000000000000000000000000000000dead");
- }
-
- @Test
- public void uriInMiddleOfInput() throws Exception {
- String prefix = "prefix ";
- String uri = "ethereum:0xfdf1210fc262c73d0436236a0e07be419babbbc4?value=42";
- String text = prefix + uri;
-
- UriMatch uriMatch = parser.parseUri(text, prefix.length());
-
- assertUriMatch(uri, uriMatch, prefix.length());
- }
-
- @Test
- public void invalidScheme() throws Exception {
- assertInvalidUri("ethereMU:0xfdf1210fc262c73d0436236a0e07be419babbbc4");
- }
-
- @Test
- public void invalidAddress() throws Exception {
- assertInvalidUri("ethereum:[invalid]");
- }
-
-
- private void assertValidUri(String uri) {
- UriMatch uriMatch = parser.parseUri(uri, 0);
- assertUriMatch(uri, uriMatch);
- }
-
- private void assertUriMatch(String uri, UriMatch uriMatch) {
- assertUriMatch(uri, uriMatch, 0);
- }
-
- private void assertUriMatch(String uri, UriMatch uriMatch, int offset) {
- assertNotNull(uriMatch);
- assertEquals(offset, uriMatch.getStartIndex());
- assertEquals(uri.length() + offset, uriMatch.getEndIndex());
- assertEquals(uri, uriMatch.getUri().toString());
- }
-
- private void assertInvalidUri(String text) {
- UriMatch uriMatch = parser.parseUri(text, 0);
- assertNull(uriMatch);
- }
-}
diff --git a/app/core/src/test/java/com/fsck/k9/message/html/GenericUriParserTest.kt b/app/core/src/test/java/com/fsck/k9/message/html/GenericUriParserTest.kt
new file mode 100644
index 000000000..4dc0b1af1
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/message/html/GenericUriParserTest.kt
@@ -0,0 +1,71 @@
+package com.fsck.k9.message.html
+
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertNotNull
+import org.junit.Test
+
+class GenericUriParserTest {
+ private val parser = GenericUriParser()
+
+ @Test
+ fun `mailto URIs`() {
+ // Examples from RFC 6068
+ assertUriValid("mailto:chris@example.com")
+ assertUriValid("mailto:infobot@example.com?subject=current-issue")
+ assertUriValid("mailto:infobot@example.com?body=send%20current-issue")
+ assertUriValid("mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index")
+ assertUriValid("mailto:list@example.org?In-Reply-To=%3C3469A91.D10AF4C@example.com%3E")
+ assertUriValid("mailto:majordomo@example.com?body=subscribe%20bamboo-l")
+ assertUriValid("mailto:joe@example.com?cc=bob@example.com&body=hello")
+ assertUriValid("mailto:gorby%25kremvax@example.com")
+ assertUriValid("mailto:unlikely%3Faddress@example.com?blat=foop")
+ assertUriValid("mailto:%22not%40me%22@example.org")
+ assertUriValid("mailto:%22oh%5C%5Cno%22@example.org")
+ assertUriValid("mailto:%22%5C%5C%5C%22it's%5C%20ugly%5C%5C%5C%22%22@example.org")
+ assertUriValid("mailto:user@example.org?subject=caf%C3%A9")
+ assertUriValid("mailto:user@example.org?subject=%3D%3Futf-8%3FQ%3Fcaf%3DC3%3DA9%3F%3D")
+ assertUriValid("mailto:user@example.org?subject=%3D%3Fiso-8859-1%3FQ%3Fcaf%3DE9%3F%3D")
+ assertUriValid("mailto:user@example.org?subject=caf%C3%A9&body=caf%C3%A9")
+ assertUriValid("mailto:user@%E7%B4%8D%E8%B1%86.example.org?subject=Test&body=NATTO")
+ }
+
+ @Test
+ fun `XMPP URIs`() {
+ // Examples from RFC 5122
+ assertUriValid("xmpp:node@example.com")
+ assertUriValid("xmpp://guest@example.com")
+ assertUriValid("xmpp://guest@example.com/support@example.com?message")
+ assertUriValid("xmpp:support@example.com?message")
+ assertUriValid("xmpp:example-node@example.com/some-resource")
+ assertUriValid("xmpp:example.com")
+ assertUriValid("xmpp:example-node@example.com?message")
+ assertUriValid("xmpp:example-node@example.com?message;subject=Hello%20World")
+ assertUriValid("xmpp:nasty!%23\$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com")
+ assertUriValid("xmpp:node@example.com/repulsive%20!%23%22\$%25&'()*+,-.%2F:;%3C=%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~resource")
+ assertUriValid("xmpp:ji%C5%99i@%C4%8Dechy.example/v%20Praze")
+ }
+
+ @Test
+ fun `matrix URIs`() {
+ // Examples from MSC 2312
+ assertUriValid("matrix:r/someroom:example.org")
+ assertUriValid("matrix:u/me:example.org")
+ assertUriValid("matrix:r/someroom:example.org/e/Arbitrary_Event_Id")
+ assertUriValid("matrix:u/her:example.org")
+ assertUriValid("matrix:u/her:example.org?action=chat")
+ assertUriValid("matrix:roomid/rid:example.org")
+ assertUriValid("matrix:r/us:example.org")
+ assertUriValid("matrix:roomid/rid:example.org?action=join&via=example2.org")
+ assertUriValid("matrix:r/us:example.org?action=join")
+ assertUriValid("matrix:r/us:example.org/e/lol823y4bcp3qo4")
+ assertUriValid("matrix:roomid/rid:example.org/event/lol823y4bcp3qo4?via=example2.org")
+ }
+
+ private fun assertUriValid(input: String) {
+ val result = parser.parseUri(input, 0)
+
+ assertNotNull(result) { uriMatch ->
+ assertThat(uriMatch.uri).isEqualTo(input)
+ }
+ }
+}
diff --git a/app/crypto-openpgp/build.gradle b/app/crypto-openpgp/build.gradle
index 671aa6439..ad360744f 100644
--- a/app/crypto-openpgp/build.gradle
+++ b/app/crypto-openpgp/build.gradle
@@ -1,11 +1,13 @@
-apply plugin: 'com.android.library'
-apply plugin: 'org.jetbrains.kotlin.android'
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
dependencies {
implementation project(":app:core")
- testImplementation "junit:junit:${versions.junit}"
- testImplementation "org.mockito:mockito-core:${versions.mockito}"
+ testImplementation libs.junit
+ testImplementation libs.mockito.core
}
android {
diff --git a/app/crypto-openpgp/src/test/java/com/fsck/k9/crypto/openpgp/EncryptionDetectorTest.java b/app/crypto-openpgp/src/test/java/com/fsck/k9/crypto/openpgp/EncryptionDetectorTest.java
index 494ac3194..992b2d169 100644
--- a/app/crypto-openpgp/src/test/java/com/fsck/k9/crypto/openpgp/EncryptionDetectorTest.java
+++ b/app/crypto-openpgp/src/test/java/com/fsck/k9/crypto/openpgp/EncryptionDetectorTest.java
@@ -12,23 +12,18 @@ import static com.fsck.k9.crypto.openpgp.MessageCreationHelper.createPart;
import static com.fsck.k9.crypto.openpgp.MessageCreationHelper.createTextMessage;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class EncryptionDetectorTest {
private static final String CRLF = "\r\n";
- private TextPartFinder textPartFinder;
private EncryptionDetector encryptionDetector;
@Before
public void setUp() {
- textPartFinder = mock(TextPartFinder.class);
-
- encryptionDetector = new EncryptionDetector(textPartFinder);
+ encryptionDetector = new EncryptionDetector(new TextPartFinder());
}
@Test
@@ -75,7 +70,6 @@ public class EncryptionDetectorTest {
"-----BEGIN PGP MESSAGE-----" + CRLF +
"some encrypted stuff here" + CRLF +
"-----END PGP MESSAGE-----");
- when(textPartFinder.findFirstTextPart(message)).thenReturn(message);
boolean encrypted = encryptionDetector.isEncrypted(message);
@@ -90,7 +84,6 @@ public class EncryptionDetectorTest {
"some encrypted stuff here" + CRLF +
"-----END PGP MESSAGE-----" + CRLF +
"epilogue");
- when(textPartFinder.findFirstTextPart(message)).thenReturn(message);
boolean encrypted = encryptionDetector.isEncrypted(message);
@@ -108,7 +101,6 @@ public class EncryptionDetectorTest {
CRLF +
"-- " + CRLF +
"my signature");
- when(textPartFinder.findFirstTextPart(message)).thenReturn(message);
boolean encrypted = encryptionDetector.isEncrypted(message);
diff --git a/app/html-cleaner/build.gradle b/app/html-cleaner/build.gradle
index e9d5a13e0..6e423bf30 100644
--- a/app/html-cleaner/build.gradle
+++ b/app/html-cleaner/build.gradle
@@ -1,10 +1,12 @@
-apply plugin: 'java-library'
-apply plugin: 'kotlin'
-apply plugin: 'com.android.lint'
+plugins {
+ id 'java-library'
+ id 'kotlin'
+ alias(libs.plugins.android.lint)
+}
dependencies {
- implementation "org.jsoup:jsoup:${versions.jsoup}"
+ implementation libs.jsoup
- testImplementation "junit:junit:${versions.junit}"
- testImplementation "com.google.truth:truth:${versions.truth}"
+ testImplementation libs.junit
+ testImplementation libs.truth
}
diff --git a/app/k9mail/build.gradle b/app/k9mail/build.gradle
index cbd49fe57..cde74c44d 100644
--- a/app/k9mail/build.gradle
+++ b/app/k9mail/build.gradle
@@ -1,11 +1,15 @@
-apply plugin: 'com.android.application'
-apply plugin: 'org.jetbrains.kotlin.android'
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
if (rootProject.testCoverage) {
apply plugin: 'jacoco'
}
dependencies {
+ coreLibraryDesugaring libs.desugar
+
implementation project(":app:ui:legacy")
implementation project(":app:ui:message-list-widget")
implementation project(":app:core")
@@ -16,29 +20,30 @@ dependencies {
implementation project(":backend:webdav")
debugImplementation project(":backend:demo")
- implementation "androidx.appcompat:appcompat:${versions.androidxAppCompat}"
- implementation "androidx.core:core-ktx:${versions.androidxCore}"
- implementation "com.takisoft.preferencex:preferencex:${versions.preferencesFix}"
- implementation "com.jakewharton.timber:timber:${versions.timber}"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}"
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.core.ktx
+ implementation libs.androidx.work.ktx
+ implementation libs.preferencex
+ implementation libs.timber
+ implementation libs.kotlinx.coroutines.core
- implementation "com.github.bumptech.glide:glide:${versions.glide}"
- annotationProcessor "com.github.bumptech.glide:compiler:${versions.glide}"
+ implementation libs.glide
+ annotationProcessor libs.glide.compiler
if (project.hasProperty('k9mail.enableLeakCanary') && project.property('k9mail.enableLeakCanary') == "true") {
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
+ debugImplementation libs.leakcanary.android
}
// Required for DependencyInjectionTest to be able to resolve OpenPgpApiManager
testImplementation project(':plugins:openpgp-api-lib:openpgp-api')
- testImplementation "org.robolectric:robolectric:${versions.robolectric}"
- testImplementation "junit:junit:${versions.junit}"
- testImplementation "com.google.truth:truth:${versions.truth}"
- testImplementation "org.mockito:mockito-inline:${versions.mockito}"
- testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}"
- testImplementation "io.insert-koin:koin-test:${versions.koin}"
- testImplementation "io.insert-koin:koin-test-junit4:${versions.koin}"
+ testImplementation libs.robolectric
+ testImplementation libs.junit
+ testImplementation libs.truth
+ testImplementation libs.mockito.inline
+ testImplementation libs.mockito.kotlin
+ testImplementation libs.koin.test
+ testImplementation libs.koin.test.junit4
}
android {
@@ -48,8 +53,8 @@ android {
applicationId "com.fsck.k9"
testApplicationId "com.fsck.k9.tests"
- versionCode 35000
- versionName '6.500-SNAPSHOT'
+ versionCode 35002
+ versionName '6.503-SNAPSHOT'
// Keep in sync with the resource string array 'supported_languages'
resConfigs "in", "br", "ca", "cs", "cy", "da", "de", "et", "en", "en_GB", "es", "eo", "eu", "fr", "gd", "gl",
@@ -66,6 +71,10 @@ android {
release
}
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ }
+
buildTypes {
release {
if (project.hasProperty('storeFile')) {
@@ -100,24 +109,32 @@ android {
}
}
- lintOptions {
+ lint {
checkDependencies true
}
packagingOptions {
- exclude 'META-INF/DEPENDENCIES'
- exclude 'META-INF/LICENSE'
- exclude 'META-INF/LICENSE.txt'
- exclude 'META-INF/NOTICE'
- exclude 'META-INF/NOTICE.txt'
- exclude 'META-INF/README'
- exclude 'META-INF/README.md'
- exclude 'META-INF/CHANGES'
- exclude 'LICENSE.txt'
- exclude 'META-INF/*.kotlin_module'
- exclude 'META-INF/*.version'
- exclude 'kotlin/**'
- exclude 'DebugProbesKt.bin'
+ jniLibs {
+ excludes += ['kotlin/**']
+ }
+
+ resources {
+ excludes += [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/LICENSE',
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE',
+ 'META-INF/NOTICE.txt',
+ 'META-INF/README',
+ 'META-INF/README.md',
+ 'META-INF/CHANGES',
+ 'LICENSE.txt',
+ 'META-INF/*.kotlin_module',
+ 'META-INF/*.version',
+ 'kotlin/**',
+ 'DebugProbesKt.bin'
+ ]
+ }
}
dependenciesInfo {
diff --git a/app/k9mail/src/main/AndroidManifest.xml b/app/k9mail/src/main/AndroidManifest.xml
index 4a246df16..9b4ccadc6 100644
--- a/app/k9mail/src/main/AndroidManifest.xml
+++ b/app/k9mail/src/main/AndroidManifest.xml
@@ -175,6 +175,9 @@
+
+
@@ -287,10 +294,12 @@
android:name=".activity.setup.OAuthFlowActivity"
android:label="@string/account_setup_basics_title" />
+
@@ -300,10 +309,12 @@
android:resource="@xml/unread_widget_info"/>
+
@@ -313,6 +324,7 @@
android:resource="@xml/message_list_widget_info" />
+
+ android:name=".notification.NotificationActionService"/>
+
+
+
+
+
+
+