diff --git a/app/autodiscovery/api/build.gradle b/app/autodiscovery/api/build.gradle index 13eabe22f4054adef7ba8b5ebcbe08bbd0aa4487..aa86ea20a971c29cbb89359e06452648b480e15d 100644 --- a/app/autodiscovery/api/build.gradle +++ b/app/autodiscovery/api/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":mail:common") diff --git a/app/autodiscovery/providersxml/build.gradle b/app/autodiscovery/providersxml/build.gradle index 1ac5253462c97e32c816bcd793748509737e24e8..65ba3c5878a499a3bc79b8e9b388c4b087a030df 100644 --- a/app/autodiscovery/providersxml/build.gradle +++ b/app/autodiscovery/providersxml/build.gradle @@ -1,23 +1,25 @@ -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") implementation project(":mail:common") implementation project(":app:autodiscovery:api") - implementation "com.jakewharton.timber:timber:${versions.timber}" + implementation libs.timber testImplementation project(':app:testing') testImplementation project(":backend:imap") - testImplementation "org.robolectric:robolectric:${versions.robolectric}" - testImplementation "androidx.test:core:${versions.androidxTestCore}" - 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.androidx.test.core + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin + testImplementation libs.koin.test + testImplementation libs.koin.test.junit4 } android { diff --git a/app/autodiscovery/srvrecords/build.gradle b/app/autodiscovery/srvrecords/build.gradle index 6ef976c06f1eb936a72543515aa0498a4400db14..70202b62f931ce2a90d5e8718c6341c24bd897de 100644 --- a/app/autodiscovery/srvrecords/build.gradle +++ b/app/autodiscovery/srvrecords/build.gradle @@ -1,14 +1,16 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":app:autodiscovery:api") - implementation "org.minidns:minidns-hla:${versions.minidns}" + implementation libs.minidns.hla - 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 libs.junit + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin } diff --git a/app/autodiscovery/thunderbird/build.gradle b/app/autodiscovery/thunderbird/build.gradle index 6f76f4e1ec5021abc13e26479ac4b4d3cb55d39e..4ef0ba73406f9dfe7780555d59fe5ce05e82c54a 100644 --- a/app/autodiscovery/thunderbird/build.gradle +++ b/app/autodiscovery/thunderbird/build.gradle @@ -1,16 +1,18 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":app:autodiscovery:api") - compileOnly 'com.github.cketti:xmlpull-extracted-from-android:1.0' - implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}" + compileOnly libs.xmlpull + implementation libs.okhttp - 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 'com.github.cketti:kxml2-extracted-from-android:1.0' + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin + testImplementation libs.kxml2 } diff --git a/app/core/build.gradle b/app/core/build.gradle index 287e36e06d32c87522fb1ff5d8593656015762de..9c8ca3079691ce9baf4a8455bd6de4319aa11474 100644 --- a/app/core/build.gradle +++ b/app/core/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: 'org.jetbrains.kotlin.plugin.parcelize' +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.parcelize) +} dependencies { api project(":mail:common") @@ -9,35 +11,37 @@ dependencies { implementation project(':plugins:openpgp-api-lib:openpgp-api') - api "io.insert-koin:koin-android:${versions.koin}" + api libs.koin.android - api "androidx.annotation:annotation:${versions.androidxAnnotation}" + api libs.androidx.annotation - implementation "com.squareup.okio:okio:${versions.okio}" - implementation "commons-io:commons-io:${versions.commonsIo}" - implementation "androidx.core:core-ktx:${versions.androidxCore}" - implementation "androidx.work:work-runtime-ktx:${versions.androidxWorkManager}" - implementation "androidx.fragment:fragment:${versions.androidxFragment}" - implementation "androidx.localbroadcastmanager:localbroadcastmanager:${versions.androidxLocalBroadcastManager}" - implementation "org.jsoup:jsoup:${versions.jsoup}" - implementation "com.squareup.moshi:moshi:${versions.moshi}" - implementation "com.jakewharton.timber:timber:${versions.timber}" - implementation "org.apache.james:apache-mime4j-core:${versions.mime4j}" + implementation libs.okio + implementation libs.commons.io + implementation libs.androidx.core.ktx + implementation libs.androidx.work.ktx + implementation libs.androidx.fragment + implementation libs.androidx.localbroadcastmanager + implementation libs.jsoup + implementation libs.moshi + implementation libs.timber + implementation libs.mime4j.core testImplementation project(':mail:testing') testImplementation project(":backend:imap") testImplementation project(":mail:protocols:smtp") testImplementation project(":app:storage") testImplementation project(":app:testing") - testImplementation "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}" - 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 "org.jdom:jdom2:2.0.6" - testImplementation "io.insert-koin:koin-test:${versions.koin}" - testImplementation "io.insert-koin:koin-test-junit4:${versions.koin}" + testImplementation libs.kotlin.test + testImplementation libs.kotlin.reflect + testImplementation libs.robolectric + testImplementation libs.androidx.test.core + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin + testImplementation libs.jdom2 + testImplementation libs.koin.test + testImplementation libs.koin.test.junit4 } android { diff --git a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java index e08a7cc9c74b96bb0e2630a5c645a68802d52d29..9f5e5a9ff5dc49a4c791cb4d508b39d9d7375503 100644 --- a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -974,11 +974,8 @@ public class MessagingController { } private void queueSetFlag(Account account, long folderId, boolean newState, Flag flag, List uids) { - putBackground("queueSetFlag", null, () -> { - PendingCommand command = PendingSetFlag.create(folderId, newState, flag, uids); - queuePendingCommand(account, command); - processPendingCommands(account); - }); + PendingCommand command = PendingSetFlag.create(folderId, newState, flag, uids); + queuePendingCommand(account, command); } /** @@ -991,11 +988,8 @@ public class MessagingController { } private void queueDelete(Account account, long folderId, List uids) { - putBackground("queueDelete", null, () -> { - PendingCommand command = PendingDelete.create(folderId, uids); - queuePendingCommand(account, command); - processPendingCommands(account); - }); + PendingCommand command = PendingDelete.create(folderId, uids); + queuePendingCommand(account, command); } void processPendingDelete(PendingDelete command, Account account) throws MessagingException { @@ -1066,12 +1060,9 @@ public class MessagingController { setFlagInCache(account, messageIds, flag, newState); - threadPool.execute(new Runnable() { - @Override - public void run() { - setFlagSynchronous(account, messageIds, flag, newState, false); - } - }); + putBackground("setFlag", null, () -> + setFlagSynchronous(account, messageIds, flag, newState, false) + ); } public void setFlagForThreads(final Account account, final List threadRootIds, @@ -1079,12 +1070,9 @@ public class MessagingController { setFlagForThreadsInCache(account, threadRootIds, flag, newState); - threadPool.execute(new Runnable() { - @Override - public void run() { - setFlagSynchronous(account, threadRootIds, flag, newState, true); - } - }); + putBackground("setFlagForThreads", null, () -> + setFlagSynchronous(account, threadRootIds, flag, newState, true) + ); } private void setFlagSynchronous(final Account account, final List ids, @@ -1306,36 +1294,49 @@ public class MessagingController { } public void markMessageAsOpened(Account account, LocalMessage message) { - put("markMessageAsOpened", null, () -> { - markMessageAsOpenedBlocking(account, message); - }); - } + threadPool.execute(() -> + notificationController.removeNewMailNotification(account, message.makeMessageReference()) + ); - private void markMessageAsOpenedBlocking(Account account, LocalMessage message) { - notificationController.removeNewMailNotification(account,message.makeMessageReference()); + if (message.isSet(Flag.SEEN)) { + // Nothing to do if the message is already marked as read + return; + } - if (!message.isSet(Flag.SEEN)) { - if (account.isMarkMessageAsReadOnView()) { - markMessageAsReadOnView(account, message); - } else { - // Marking a message as read will automatically mark it as "not new". But if we don't mark the message - // as read on opening, we have to manually mark it as "not new". - markMessageAsNotNew(account, message); + boolean markMessageAsRead = account.isMarkMessageAsReadOnView(); + if (markMessageAsRead) { + // Mark the message itself as read right away + try { + message.setFlagInternal(Flag.SEEN, true); + } catch (MessagingException e) { + Timber.e(e, "Error while marking message as read"); } - } - } - private void markMessageAsReadOnView(Account account, LocalMessage message) { - try { + // Also mark the message as read in the cache List messageIds = Collections.singletonList(message.getDatabaseId()); - setFlag(account, messageIds, Flag.SEEN, true); + setFlagInCache(account, messageIds, Flag.SEEN, true); + } - message.setFlagInternal(Flag.SEEN, true); - } catch (MessagingException e) { - Timber.e(e, "Error while marking message as read"); + putBackground("markMessageAsOpened", null, () -> { + markMessageAsOpenedBlocking(account, message, markMessageAsRead); + }); + } + + private void markMessageAsOpenedBlocking(Account account, LocalMessage message, boolean markMessageAsRead) { + if (markMessageAsRead) { + markMessageAsRead(account, message); + } else { + // Marking a message as read will automatically mark it as "not new". But if we don't mark the message + // as read on opening, we have to manually mark it as "not new". + markMessageAsNotNew(account, message); } } + private void markMessageAsRead(Account account, LocalMessage message) { + List messageIds = Collections.singletonList(message.getDatabaseId()); + setFlagSynchronous(account, messageIds, Flag.SEEN, true, false); + } + private void markMessageAsNotNew(Account account, LocalMessage message) { MessageStore messageStore = messageStoreManager.getMessageStore(account); long folderId = message.getFolder().getDatabaseId(); @@ -2051,10 +2052,13 @@ public class MessagingController { Map uidMap = null; Long trashFolderId = account.getTrashFolderId(); boolean isSpamFolder = account.hasSpamFolder() && account.getSpamFolderId() == folderId; + boolean doNotMoveToTrashFolder = skipTrashFolder || + !account.hasTrashFolder() || folderId == trashFolderId || + isSpamFolder || + (backend.getSupportsTrashFolder() && !backend.isDeleteMoveToTrash()); LocalFolder localTrashFolder = null; - if (skipTrashFolder || !account.hasTrashFolder() || folderId == trashFolderId || isSpamFolder || - (backend.getSupportsTrashFolder() && !backend.isDeleteMoveToTrash())) { + if (doNotMoveToTrashFolder) { Timber.d("Not moving deleted messages to local Trash folder. Removing local copies."); if (!localOnlyMessages.isEmpty()) { @@ -2118,8 +2122,7 @@ public class MessagingController { // Nothing to do on the remote side } else if (!syncedMessageUids.isEmpty()) { if (account.getDeletePolicy() == DeletePolicy.ON_DELETE) { - if (!account.hasTrashFolder() || folderId == trashFolderId || - !backend.isDeleteMoveToTrash()) { + if (doNotMoveToTrashFolder) { queueDelete(account, folderId, syncedMessageUids); } else if (account.isMarkMessageAsReadOnDelete()) { queueMoveOrCopy(account, folderId, trashFolderId, 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 3f846a6c6c8e1b96f3aeda29742a2958bb26b5cf..01af19471925933612cc64ca48cbae20b07cff19 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/crypto-openpgp/build.gradle b/app/crypto-openpgp/build.gradle index 671aa64390ee338b61443a5b009e780bcc9547f0..ad360744f3f9ed4446ce72145cb7f6513698161d 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/html-cleaner/build.gradle b/app/html-cleaner/build.gradle index e9d5a13e0d7f381647741d12f844da33c55203f5..6e423bf30ec6b00e6b2fe0794bce2b7bf7b33f43 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/html-cleaner/src/main/java/app/k9mail/html/cleaner/HeadCleaner.kt b/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/HeadCleaner.kt index d9ac70f810c7b9b7ac3dc705a5444b4ff682ea22..1485291428dd46e16cc1c873d9436978be18c365 100644 --- a/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/HeadCleaner.kt +++ b/app/html-cleaner/src/main/java/app/k9mail/html/cleaner/HeadCleaner.kt @@ -9,7 +9,7 @@ import org.jsoup.parser.Tag import org.jsoup.select.NodeTraversor import org.jsoup.select.NodeVisitor -private val ALLOWED_TAGS = listOf("style", "meta") +private val ALLOWED_TAGS = listOf("style", "meta", "base") internal class HeadCleaner { fun clean(dirtyDocument: Document, cleanedDocument: Document) { diff --git a/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt b/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt index d60ae8b381240b6354c8ed3f3370845143c688bb..902500006711810f5f730260cdf78c373599763f 100644 --- a/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt +++ b/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt @@ -465,6 +465,25 @@ class HtmlSanitizerTest { assertTagsNotStripped("var") } + @Test + fun `should keep 'base' element`() { + val html = + """ + + + + + + Link + + + """.compactHtml() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + private fun assertTagsNotStripped(element: String) { val html = """<$element>some text""" @@ -491,4 +510,6 @@ class HtmlSanitizerTest { } private fun String.trimLineBreaks() = replace("\n", "") + + private fun String.compactHtml() = lines().joinToString(separator = "") { it.trim() } } diff --git a/app/k9mail-jmap/src/main/res/values/themes.xml b/app/k9mail-jmap/src/main/res/values/themes.xml index d0d0c1119e99f756f7334246bb4183b2e3026cfd..4afbec42457f45f9b20ba868b2cd83e222465e0c 100644 --- a/app/k9mail-jmap/src/main/res/values/themes.xml +++ b/app/k9mail-jmap/src/main/res/values/themes.xml @@ -16,12 +16,14 @@ @color/material_gray_100 @color/material_gray_100 - @color/material_blue_600 - @color/material_blue_800 - @color/material_pink_400 - @color/material_pink_200 + @color/material_gray_800 + @color/material_gray_700 + @color/material_pink_500 + @color/material_pink_300 #ffffff @color/material_gray_50 + ?attr/colorAccent + ?android:attr/colorBackground @style/Widget.K9.Toolbar @style/PreferenceThemeOverlay @@ -88,13 +90,18 @@ @drawable/ic_import_status @android:color/primary_text_light @android:color/secondary_text_light + ?attr/colorAccent #8038B8E2 + 33% + ?attr/colorSurface ?android:attr/windowBackground #c0cdcdcd #DCDCDC ?android:attr/colorBackground @drawable/thread_count_box_light - #ff2ea7d1 + ?attr/colorSecondaryVariant + 60% + ?attr/colorSurface #ff696969 #ffcccccc #bbbbbb @@ -171,11 +178,13 @@ @color/material_gray_900 @color/material_gray_900 - @color/material_blue_400 - @color/material_blue_600 + @color/material_gray_100 + @color/material_gray_50 @color/material_pink_300 @color/material_pink_500 @color/material_gray_900 + ?attr/colorAccent + ?android:attr/colorBackground @style/Widget.K9.Toolbar @style/PreferenceThemeOverlay @@ -242,13 +251,18 @@ @drawable/ic_import_status @android:color/primary_text_dark @android:color/secondary_text_dark + ?attr/colorAccent #8038B8E2 + 25% + ?attr/colorSurface ?android:attr/windowBackground #00000000 #2F2F2F ?android:attr/colorBackground @drawable/thread_count_box_dark - #ff33b5e5 + ?attr/colorSecondaryVariant + 50% + ?attr/colorSurface #ffa0a0a0 #ff333333 #777777 diff --git a/app/k9mail/build.gradle b/app/k9mail/build.gradle index 3d7107d2697d9698ad4660a434b7d6152526bc26..12b921e8fc7603839da11eb0e36b9991dbe01f09 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,32 +20,33 @@ dependencies { implementation project(":backend:webdav") debugImplementation project(":backend:demo") - implementation "androidx.appcompat:appcompat:${versions.androidxAppCompat}" - implementation "androidx.core:core-ktx:${versions.androidxCore}" - implementation "androidx.work:work-runtime-ktx:${versions.androidxWorkManager}" - 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 libs.glide - implementation "com.github.bumptech.glide:glide:${versions.glide}" - annotationProcessor "com.github.bumptech.glide:compiler:${versions.glide}" + implementation libs.elib + + annotationProcessor libs.glide.compiler if (project.hasProperty('k9mail.enableLeakCanary') && project.property('k9mail.enableLeakCanary') == "true") { - debugImplementation "com.squareup.leakcanary:leakcanary-android:${versions.leakcanary}" + 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}" - - implementation 'foundation.e:elib:0.0.1-alpha11' + 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 { @@ -51,8 +56,8 @@ android { applicationId "foundation.e.mail" testApplicationId "foundation.e.mail.tests" - versionCode 35002 - versionName '6.502' + versionCode 35003 + versionName '6.503' // 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", @@ -69,6 +74,10 @@ android { release } + compileOptions { + coreLibraryDesugaringEnabled true + } + buildTypes { release { if (project.hasProperty('storeFile')) { @@ -103,24 +112,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/res/values/themes.xml b/app/k9mail/src/main/res/values/themes.xml index 02150d4587c7e5757fd663d80135c54a693bb9af..abd4094b014c3ca656327f0c615f738d49b4844e 100644 --- a/app/k9mail/src/main/res/values/themes.xml +++ b/app/k9mail/src/main/res/values/themes.xml @@ -111,6 +111,7 @@ @drawable/thread_count_box_light @color/color_default_accent @color/color_default_ternary_text + @color/color_default_background @color/color_default_divider @color/default_icon_color @drawable/ic_messagelist_attachment @@ -128,6 +129,9 @@ #77aa22 #dd2222 #888 + ?attr/colorAccent + ?android:attr/colorBackground + ?attr/colorAccent @drawable/ic_info @drawable/ic_people @@ -257,6 +261,7 @@ @drawable/thread_count_box_light @color/color_default_accent @color/color_default_ternary_text + @color/color_default_background @color/color_default_divider #bbbbbb @drawable/ic_messagelist_attachment @@ -273,6 +278,9 @@ #77aa22 #dd2222 #888 + ?attr/colorAccent + ?android:attr/colorBackground + ?attr/colorAccent @drawable/ic_info @drawable/ic_people diff --git a/app/storage/build.gradle b/app/storage/build.gradle index 7f270f394f5d1996bd61d2364c21a19dc78f0595..d5a8b51c11868b82957cd123ece43ebb0f791aef 100644 --- a/app/storage/build.gradle +++ b/app/storage/build.gradle @@ -1,26 +1,28 @@ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} dependencies { - api "io.insert-koin:koin-core:${versions.koin}" + api libs.koin.core implementation project(":app:core") - implementation "androidx.core:core-ktx:${versions.androidxCore}" - implementation "com.jakewharton.timber:timber:${versions.timber}" - implementation "org.apache.james:apache-mime4j-core:${versions.mime4j}" - implementation "commons-io:commons-io:${versions.commonsIo}" - implementation "com.squareup.moshi:moshi:${versions.moshi}" + implementation libs.androidx.core.ktx + implementation libs.timber + implementation libs.mime4j.core + implementation libs.commons.io + implementation libs.moshi testImplementation project(':mail:testing') testImplementation project(':app:testing') - 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 "commons-io:commons-io:${versions.commonsIo}" + 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 + testImplementation libs.commons.io } android { diff --git a/app/testing/build.gradle b/app/testing/build.gradle index 181203c2abfcfcb5bee3eea01e99d2ffe222c346..51ee7fd2609aae9bc0cfc445c0afbf6c1a9ac9b5 100644 --- a/app/testing/build.gradle +++ b/app/testing/build.gradle @@ -1,14 +1,16 @@ -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') - api "junit:junit:${versions.junit}" - api "org.robolectric:robolectric:${versions.robolectric}" - api "io.insert-koin:koin-core:${versions.koin}" - api "org.mockito:mockito-core:${versions.mockito}" - api "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}" + api libs.junit + api libs.robolectric + api libs.koin.core + api libs.mockito.core + api libs.mockito.kotlin } android { diff --git a/app/ui/base/build.gradle b/app/ui/base/build.gradle index da4e97904fb2e954b505b8948edb0f62524e2093..f93989972842c43abc585fd1d830bbef6f1a1495 100644 --- a/app/ui/base/build.gradle +++ b/app/ui/base/build.gradle @@ -1,20 +1,22 @@ -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") - api "androidx.appcompat:appcompat:${versions.androidxAppCompat}" - api "androidx.activity:activity:${versions.androidxActivity}" - api "com.google.android.material:material:${versions.materialComponents}" - api "androidx.navigation:navigation-fragment:${versions.androidxNavigation}" - api "androidx.navigation:navigation-ui:${versions.androidxNavigation}" - api "androidx.lifecycle:lifecycle-livedata-ktx:${versions.androidxLifecycle}" + api libs.androidx.appcompat + api libs.androidx.activity + api libs.android.material + api libs.androidx.navigation.fragment + api libs.androidx.navigation.ui + api libs.androidx.lifecycle.livedata.ktx - implementation "androidx.core:core-ktx:${versions.androidxCore}" - implementation "androidx.biometric:biometric:${versions.androidxBiometric}" - implementation "com.jakewharton.timber:timber:${versions.timber}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}" + implementation libs.androidx.core.ktx + implementation libs.androidx.biometric + implementation libs.timber + implementation libs.kotlinx.coroutines.core } android { diff --git a/app/ui/legacy/build.gradle b/app/ui/legacy/build.gradle index ad20259205718f582e20bc7d5a9aa21d3f2d7317..c52e713da15f408d5d753942aa5396b2f85fa95b 100644 --- a/app/ui/legacy/build.gradle +++ b/app/ui/legacy/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: 'org.jetbrains.kotlin.plugin.parcelize' +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.parcelize) +} dependencies { api project(":app:ui:base") @@ -16,62 +18,61 @@ dependencies { implementation project(':plugins:openpgp-api-lib:openpgp-api') - implementation "androidx.appcompat:appcompat:${versions.androidxAppCompat}" - implementation "androidx.preference:preference:${versions.androidxPreference}" - implementation "com.takisoft.preferencex:preferencex:${versions.preferencesFix}" - implementation "com.takisoft.preferencex:preferencex-datetimepicker:${versions.preferencesFix}" - implementation "com.takisoft.preferencex:preferencex-colorpicker:${versions.preferencesFix}" - implementation "androidx.recyclerview:recyclerview:${versions.androidxRecyclerView}" + implementation libs.androidx.appcompat + implementation libs.androidx.preference + implementation libs.preferencex + implementation libs.preferencex.datetimepicker + implementation libs.preferencex.colorpicker + implementation libs.androidx.recyclerview implementation project(':ui-utils:LinearLayoutManager') implementation project(':ui-utils:ItemTouchHelper') - implementation "androidx.lifecycle:lifecycle-runtime-ktx:${versions.androidxLifecycle}" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${versions.androidxLifecycle}" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:${versions.androidxLifecycle}" - implementation "androidx.constraintlayout:constraintlayout:${versions.androidxConstraintLayout}" - implementation "androidx.cardview:cardview:${versions.androidxCardView}" - implementation "androidx.localbroadcastmanager:localbroadcastmanager:${versions.androidxLocalBroadcastManager}" - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation "de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02" - implementation "com.splitwise:tokenautocomplete:3.0.2" - implementation "de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0" - implementation 'com.mikepenz:materialdrawer:8.4.5' - implementation 'com.github.ByteHamster:SearchPreference:v2.3.0' - implementation "com.mikepenz:fastadapter:${versions.fastAdapter}" - implementation "com.mikepenz:fastadapter-extensions-drag:${versions.fastAdapter}" - implementation "com.mikepenz:fastadapter-extensions-utils:${versions.fastAdapter}" - implementation 'de.hdodenhof:circleimageview:3.1.0' - api 'net.openid:appauth:0.11.1' + implementation libs.androidx.lifecycle.runtime.ktx + implementation libs.androidx.lifecycle.viewmodel.ktx + implementation libs.androidx.lifecycle.livedata.ktx + implementation libs.androidx.constraintlayout + implementation libs.androidx.localbroadcastmanager + implementation libs.androidx.swiperefreshlayout + implementation libs.ckchangelog.core + implementation libs.tokenautocomplete + implementation libs.safeContentResolver + implementation libs.materialdrawer + implementation libs.searchPreference + implementation libs.fastadapter + implementation libs.fastadapter.extensions.drag + implementation libs.fastadapter.extensions.utils + implementation libs.circleimageview + api libs.appauth + + implementation libs.commons.io + implementation libs.androidx.core.ktx + implementation libs.jcip.annotations + implementation libs.timber + implementation libs.mime4j.core + implementation libs.kotlinx.coroutines.core + implementation libs.kotlinx.coroutines.android + + implementation libs.glide - implementation "commons-io:commons-io:${versions.commonsIo}" - implementation "androidx.core:core-ktx:${versions.androidxCore}" - implementation "net.jcip:jcip-annotations:1.0" - implementation "com.jakewharton.timber:timber:${versions.timber}" - implementation "org.apache.james:apache-mime4j-core:${versions.mime4j}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlinCoroutines}" + implementation libs.retrofit + implementation libs.retrofit.converter.simplexml + implementation libs.fullscreenloadingdialog - implementation "com.github.bumptech.glide:glide:${versions.glide}" - annotationProcessor "com.github.bumptech.glide:compiler:${versions.glide}" + annotationProcessor libs.glide.compiler testImplementation project(':mail:testing') testImplementation project(':app:storage') testImplementation project(':app:testing') - testImplementation "org.robolectric:robolectric:${versions.robolectric}" - testImplementation "androidx.test:core:${versions.androidxTestCore}" - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.jetbrains.kotlin:kotlin-test:${versions.kotlin}" - 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 "org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.kotlinCoroutines}" - testImplementation "app.cash.turbine:turbine:${versions.turbine}" - - implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}" - implementation "com.squareup.retrofit2:converter-simplexml:${versions.retrofit}" - - implementation "com.github.fahim44:FullScreenLoadingDialog:1.0.7" + testImplementation libs.robolectric + testImplementation libs.androidx.test.core + testImplementation libs.junit + testImplementation libs.kotlin.test + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin + testImplementation libs.koin.test + testImplementation libs.koin.test.junit4 + testImplementation libs.kotlinx.coroutines.test + testImplementation libs.turbine } android { diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/ThemeExtensions.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/ThemeExtensions.kt index d8aa489e09ead382e9854268ef144a1082751e5f..51c73e2d0a6b1206da0377c718a22c68afebc05d 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/ThemeExtensions.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/ThemeExtensions.kt @@ -1,6 +1,7 @@ package com.fsck.k9.ui import android.content.res.Resources.Theme +import android.graphics.Color import android.graphics.drawable.Drawable import android.util.TypedValue @@ -15,6 +16,32 @@ fun Theme.resolveColorAttribute(attrId: Int): Int { return typedValue.data } +fun Theme.resolveColorAttribute(colorAttrId: Int, alphaFractionAttrId: Int, backgroundColorAttrId: Int): Int { + val typedValue = TypedValue() + + if (!resolveAttribute(colorAttrId, typedValue, true)) { + error("Couldn't resolve attribute ($colorAttrId)") + } + val color = typedValue.data + + if (!resolveAttribute(alphaFractionAttrId, typedValue, true)) { + error("Couldn't resolve attribute ($alphaFractionAttrId)") + } + val colorPercentage = TypedValue.complexToFloat(typedValue.data) + val backgroundPercentage = 1 - colorPercentage + + if (!resolveAttribute(backgroundColorAttrId, typedValue, true)) { + error("Couldn't resolve attribute ($colorAttrId)") + } + val backgroundColor = typedValue.data + + val red = colorPercentage * Color.red(color) + backgroundPercentage * Color.red(backgroundColor) + val green = colorPercentage * Color.green(color) + backgroundPercentage * Color.green(backgroundColor) + val blue = colorPercentage * Color.blue(color) + backgroundPercentage * Color.blue(backgroundColor) + + return Color.rgb(red.toInt(), green.toInt(), blue.toInt()) +} + fun Theme.resolveDrawableAttribute(attrId: Int): Drawable { val typedValue = TypedValue() diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt index 0c87cf8530c46937b81079d233a84521d0f98bda..9078196b666a13698bcf6214f78d356eaf6fe0c6 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAdapter.kt @@ -54,7 +54,7 @@ class MessageListAdapter internal constructor( private val activeItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListActiveItemBackgroundColor) private val selectedItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListSelectedBackgroundColor) private val previewTextColor: Int = theme.resolveColorAttribute(R.attr.messageListPreviewTextColor) - private val regularItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListUnreadItemBackgroundColor) + private val regularItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListRegularItemBackgroundColor) private val readItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListReadItemBackgroundColor) private val unreadItemBackgroundColor: Int = theme.resolveColorAttribute(R.attr.messageListUnreadItemBackgroundColor) diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentView.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentView.java index fbacf67bd97653ad8f981634ad5d3404b473824a..400a29dc7346f8c9c2b726d887d16da199c741c9 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentView.java +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/AttachmentView.java @@ -25,6 +25,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener { private AttachmentViewInfo attachment; private AttachmentViewCallback callback; + private View cardView; private View saveButton; private ImageView preview; private ImageView attachmentType; @@ -46,6 +47,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener { @Override protected void onFinishInflate() { super.onFinishInflate(); + cardView = findViewById(R.id.attachment_card); saveButton = findViewById(R.id.save_button); preview = findViewById(R.id.attachment_preview); attachmentType = findViewById(R.id.attachment_type); @@ -72,7 +74,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener { saveButton.setVisibility(View.INVISIBLE); } - setOnClickListener(this); + cardView.setOnClickListener(this); saveButton.setOnClickListener(this); TextView attachmentName = findViewById(R.id.attachment_name); @@ -102,7 +104,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener { @Override public void onClick(View view) { - if (view == this) { + if (view.getId() == R.id.attachment_card) { onViewButtonClick(); } else if (view.getId() == R.id.save_button) { onSaveButtonClick(); diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt index 41629b9b6dd5f9dbc375e9f3edfbbe7f9b1ef3ba..5848703a7d39afe6984da48817cf4ec07408b9de 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/onboarding/WelcomeFragment.kt @@ -15,11 +15,11 @@ import com.fsck.k9.ui.observeNotNull import com.fsck.k9.ui.settings.import.SettingsImportResultViewModel import com.fsck.k9.ui.settings.import.SettingsImportSuccess import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel class WelcomeFragment : Fragment() { private val htmlToSpanned: HtmlToSpanned by inject() - private val importResultViewModel: SettingsImportResultViewModel by sharedViewModel() + private val importResultViewModel: SettingsImportResultViewModel by activityViewModel() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_welcome_message, container, false) diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt index b79ef71701f78573a69c401719082ec310c97bb3..8ae3d7cabe4360d97e98c82cf9c0d8a1ee7565e5 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt @@ -39,14 +39,14 @@ import com.fsck.k9.ui.settings.removeEntry import com.fsck.k9.ui.withArguments import com.takisoft.preferencex.PreferenceFragmentCompat import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel import org.koin.core.parameter.parametersOf import org.openintents.openpgp.OpenPgpApiManager import org.openintents.openpgp.util.OpenPgpKeyPreference import org.openintents.openpgp.util.OpenPgpProviderUtil class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFragmentListener { - private val viewModel: AccountSettingsViewModel by sharedViewModel() + private val viewModel: AccountSettingsViewModel by activityViewModel() private val dataStoreFactory: AccountSettingsDataStoreFactory by inject() private val openPgpApiManager: OpenPgpApiManager by inject { parametersOf(this) } private val messagingController: MessagingController by inject() diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt index 330b1b5711078c250ea235a5917eef507283f83d..4b3399dcbe21063291a8ebaaca4ff8dd19d83de3 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/import/SettingsImportFragment.kt @@ -19,12 +19,12 @@ import com.fsck.k9.ui.R import com.fsck.k9.ui.observeNotNull import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.adapters.ItemAdapter -import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.activityViewModel import org.koin.androidx.viewmodel.ext.android.viewModel class SettingsImportFragment : Fragment() { private val viewModel: SettingsImportViewModel by viewModel() - private val resultViewModel: SettingsImportResultViewModel by sharedViewModel() + private val resultViewModel: SettingsImportResultViewModel by activityViewModel() private lateinit var settingsImportAdapter: FastAdapter> private lateinit var itemAdapter: ItemAdapter> diff --git a/app/ui/legacy/src/main/res/layout/message_compose_attachment.xml b/app/ui/legacy/src/main/res/layout/message_compose_attachment.xml index 565f7b50cc038d22c315fe713a65ba359a8ebe5d..c33b686db3026a2ccfdfa59933d2d4a53e81387d 100644 --- a/app/ui/legacy/src/main/res/layout/message_compose_attachment.xml +++ b/app/ui/legacy/src/main/res/layout/message_compose_attachment.xml @@ -1,5 +1,5 @@ - - + diff --git a/app/ui/legacy/src/main/res/layout/message_list_fragment.xml b/app/ui/legacy/src/main/res/layout/message_list_fragment.xml index c8e1d739a77ca3c4d1b91d2aeb5935e38fe2dad3..a0b3a4be87441061454ce5f3b231838f2f3d264c 100644 --- a/app/ui/legacy/src/main/res/layout/message_list_fragment.xml +++ b/app/ui/legacy/src/main/res/layout/message_list_fragment.xml @@ -34,10 +34,10 @@ android:layout_gravity="bottom|end" android:layout_margin="@dimen/floatingActionButtonMargin" android:contentDescription="@string/compose_action" - android:backgroundTint="@color/color_default_accent" android:text="@string/compose_action" - app:iconTint="@color/color_default_background" - android:textColor="@color/color_default_background" - app:icon="?attr/iconFloatingCompose" /> + android:textColor="?attr/floatingActionButtonForegroundColor" + app:backgroundTint="?attr/floatingActionButtonBackgroundColor" + app:icon="?attr/iconFloatingCompose" + app:iconTint="?attr/floatingActionButtonForegroundColor" /> diff --git a/app/ui/legacy/src/main/res/layout/message_view_attachment.xml b/app/ui/legacy/src/main/res/layout/message_view_attachment.xml index 8b1784a34f705e139d681822bd0b6c305f52f1ce..270a2232aae4972f938fcaeda612a44d0c17cf96 100644 --- a/app/ui/legacy/src/main/res/layout/message_view_attachment.xml +++ b/app/ui/legacy/src/main/res/layout/message_view_attachment.xml @@ -7,16 +7,17 @@ android:paddingBottom="4dp" android:id="@+id/attachment"> - - + diff --git a/app/ui/legacy/src/main/res/layout/message_view_attachment_locked.xml b/app/ui/legacy/src/main/res/layout/message_view_attachment_locked.xml index dd85384899e9b2e7c1fcd81b0133f162d6a17764..5605d7b5683a412b51d8f61978e098106dba0783 100644 --- a/app/ui/legacy/src/main/res/layout/message_view_attachment_locked.xml +++ b/app/ui/legacy/src/main/res/layout/message_view_attachment_locked.xml @@ -8,7 +8,7 @@ android:outAnimation="@anim/fade_out" app:previewInitialChild="0"> - - + /changelog.xml. --> + + Tweaked theme colors + Added support for HTML messages using the <base> tag and relative links + Fixed the logic to skip the trash folder when deleting messages, e.g. when deleting spam + Fixed bug that could lead to messages not being marked as read on the server when they should have been + Internal changes + Fixed crash at app startup diff --git a/app/ui/legacy/src/main/res/values/attrs.xml b/app/ui/legacy/src/main/res/values/attrs.xml index 00685d776b5f89f94bb8bbdb2c82ba4353277672..cb7a3357992b12048c3382b6d06c9072f1332a19 100644 --- a/app/ui/legacy/src/main/res/values/attrs.xml +++ b/app/ui/legacy/src/main/res/values/attrs.xml @@ -5,6 +5,8 @@ + + @@ -67,13 +69,18 @@ + + + + + diff --git a/app/ui/message-list-widget/build.gradle b/app/ui/message-list-widget/build.gradle index cf43f30db74e2ee3215da2fec7d855ed2f16ca0e..ef62929f4189cbc5d4a3c89e04f4bb413fdd761b 100644 --- a/app/ui/message-list-widget/build.gradle +++ b/app/ui/message-list-widget/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:ui:legacy") implementation project(":app:core") - implementation "com.jakewharton.timber:timber:${versions.timber}" + implementation libs.timber } android { diff --git a/app/ui/setup/build.gradle b/app/ui/setup/build.gradle index 092bc160b7fa861e23f744daf28c22d74212b6a6..3585042da50e5440f87d8479d7b6859f8471bbef 100644 --- a/app/ui/setup/build.gradle +++ b/app/ui/setup/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} dependencies { api project(":app:ui:base") @@ -7,23 +9,23 @@ dependencies { implementation project(":app:autodiscovery:api") implementation project(":mail:common") - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${versions.androidxLifecycle}" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:${versions.androidxLifecycle}" - implementation "androidx.constraintlayout:constraintlayout:${versions.androidxConstraintLayout}" - implementation "androidx.core:core-ktx:${versions.androidxCore}" - implementation "com.jakewharton.timber:timber:${versions.timber}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlinCoroutines}" + implementation libs.androidx.lifecycle.viewmodel.ktx + implementation libs.androidx.lifecycle.livedata.ktx + implementation libs.androidx.constraintlayout + implementation libs.androidx.core.ktx + implementation libs.timber + implementation libs.kotlinx.coroutines.core + implementation libs.kotlinx.coroutines.android testImplementation project(':mail:testing') testImplementation project(':app:testing') - testImplementation "org.robolectric:robolectric:${versions.robolectric}" - testImplementation "junit:junit:${versions.junit}" - testImplementation "com.google.truth:truth:${versions.truth}" - testImplementation "org.mockito:mockito-core:${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.core + testImplementation libs.mockito.kotlin + testImplementation libs.koin.test + testImplementation libs.koin.test.junit4 } android { diff --git a/backend/api/build.gradle b/backend/api/build.gradle index 13eabe22f4054adef7ba8b5ebcbe08bbd0aa4487..aa86ea20a971c29cbb89359e06452648b480e15d 100644 --- a/backend/api/build.gradle +++ b/backend/api/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":mail:common") diff --git a/backend/demo/build.gradle b/backend/demo/build.gradle index 00031f89e24f138970aa60e332a453956616b2e1..4e1ea924c778d084e9a4c6a609b6ffd996be278c 100644 --- a/backend/demo/build.gradle +++ b/backend/demo/build.gradle @@ -1,17 +1,19 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.google.devtools.ksp' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.ksp) + alias(libs.plugins.android.lint) +} dependencies { api project(":backend:api") - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}" - implementation "com.squareup.moshi:moshi:${versions.moshi}" - ksp "com.squareup.moshi:moshi-kotlin-codegen:${versions.moshi}" + implementation libs.kotlinx.coroutines.core + implementation libs.moshi + ksp libs.moshi.kotlin.codegen testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.mockito:mockito-core:${versions.mockito}" - testImplementation "com.google.truth:truth:${versions.truth}" + testImplementation libs.junit + testImplementation libs.mockito.core + testImplementation libs.truth } diff --git a/backend/imap/build.gradle b/backend/imap/build.gradle index 013afae0f0e27b95879a139d3e806bc244f0fa3b..745177e0452aba5019d6638459cac2624e3705b7 100644 --- a/backend/imap/build.gradle +++ b/backend/imap/build.gradle @@ -1,19 +1,21 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":backend:api") api project(":mail:protocols:imap") api project(":mail:protocols:smtp") - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinCoroutines}" + implementation libs.kotlinx.coroutines.core testImplementation project(":mail:testing") testImplementation project(":backend:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.mockito:mockito-inline:${versions.mockito}" - testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}" - testImplementation "com.google.truth:truth:${versions.truth}" - testImplementation "org.apache.james:apache-mime4j-dom:${versions.mime4j}" + testImplementation libs.junit + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin + testImplementation libs.truth + testImplementation libs.mime4j.dom } diff --git a/backend/jmap/build.gradle b/backend/jmap/build.gradle index 318691869a64ddd6234db7822a170dc3269bafc7..700abb790b7b6340b7f81b0bc6f7546f56468217 100644 --- a/backend/jmap/build.gradle +++ b/backend/jmap/build.gradle @@ -1,18 +1,20 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.google.devtools.ksp' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.ksp) + alias(libs.plugins.android.lint) +} dependencies { api project(":backend:api") - api "com.squareup.okhttp3:okhttp:${versions.okhttp}" - implementation "rs.ltt.jmap:jmap-client:0.3.1" - implementation "com.squareup.moshi:moshi:${versions.moshi}" - ksp "com.squareup.moshi:moshi-kotlin-codegen:${versions.moshi}" + api libs.okhttp + implementation libs.jmap.client + implementation libs.moshi + ksp libs.moshi.kotlin.codegen testImplementation project(":mail:testing") testImplementation project(':backend:testing') - testImplementation "org.mockito:mockito-core:${versions.mockito}" - testImplementation("com.squareup.okhttp3:mockwebserver:${versions.okhttp}") + testImplementation libs.mockito.core + testImplementation libs.okhttp.mockwebserver } diff --git a/backend/pop3/build.gradle b/backend/pop3/build.gradle index 40731fbf717b4984979805b0bf614fae4b117083..dfd503ead6a0cb113e9abf94f8e0323449949420 100644 --- a/backend/pop3/build.gradle +++ b/backend/pop3/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":backend:api") @@ -8,6 +10,6 @@ dependencies { api project(":mail:protocols:smtp") testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.mockito:mockito-core:${versions.mockito}" + testImplementation libs.junit + testImplementation libs.mockito.core } diff --git a/backend/testing/build.gradle b/backend/testing/build.gradle index f805834824551d2746534f1e0bc6e8dd7909eab0..b1a4dddf5fa70c038be74ad9ea8b49697d315392 100644 --- a/backend/testing/build.gradle +++ b/backend/testing/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 project(":backend:api") - implementation "com.squareup.okio:okio:${versions.okio}" - implementation "junit:junit:${versions.junit}" + implementation libs.okio + implementation libs.junit } diff --git a/backend/webdav/build.gradle b/backend/webdav/build.gradle index 18c1f091cc0650aced75ac6acdb079a4c23a5d6f..706ab2b7fdd2f75c85592e93bd10a90c6499c8a7 100644 --- a/backend/webdav/build.gradle +++ b/backend/webdav/build.gradle @@ -1,12 +1,14 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} dependencies { api project(":backend:api") api project(":mail:protocols:webdav") testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.mockito:mockito-core:${versions.mockito}" + testImplementation libs.junit + testImplementation libs.mockito.core } diff --git a/backend/webdav/src/main/AndroidManifest.xml b/backend/webdav/src/main/AndroidManifest.xml deleted file mode 100644 index 7299b9e31b1e15b984d4cb08d786d15166630162..0000000000000000000000000000000000000000 --- a/backend/webdav/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/build.gradle b/build.gradle index 73378d05e1fd236f96dc0c259d3128d238481708..8bf6d7771935aec41396bfff1e2c57f03b2bacd7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,78 +1,15 @@ import com.android.build.gradle.BasePlugin -import org.gradle.api.plugins.JavaPlugin import org.jetbrains.kotlin.gradle.dsl.KotlinCompile -buildscript { - ext { - // Judging the impact of newer library versions on the app requires being intimately familiar with the code - // base. Please don't open pull requests upgrading dependencies if you're a new contributor. - versions = [ - 'kotlin': '1.7.22', - 'kotlinCoroutines': '1.6.4', - 'jetbrainsAnnotations': '23.0.0', - 'androidxAppCompat': '1.5.1', - 'androidxActivity': '1.6.0', - 'androidxRecyclerView': '1.2.1', - 'androidxLifecycle': '2.5.1', - 'androidxAnnotation': '1.5.0', - 'androidxBiometric': '1.1.0', - 'androidxNavigation': '2.5.2', - 'androidxConstraintLayout': '2.1.4', - 'androidxWorkManager': '2.7.1', - 'androidxFragment': '1.5.3', - 'androidxLocalBroadcastManager': '1.1.0', - 'androidxCore': '1.9.0', - 'androidxCardView': '1.0.0', - 'androidxPreference': '1.2.0', - 'androidxDrawerLayout': '1.1.1', - 'androidxTransition': '1.4.1', - 'androidxTestCore': '1.4.0', - 'materialComponents': '1.6.1', - 'fastAdapter': '5.7.0', - 'preferencesFix': '1.1.0', - 'okio': '3.2.0', - 'moshi': '1.14.0', - 'timber': '5.0.1', - 'koin': '3.2.2', - // We can't upgrade Commons IO beyond this version because starting with 2.7 it is using Java 8 API - // that is not available until Android API 26 (even with desugaring enabled). - // See https://issuetracker.google.com/issues/160484830 - 'commonsIo': '2.6', - 'mime4j': '0.8.6', - 'okhttp': '4.10.0', - 'minidns': '1.0.4', - 'retrofit': '2.9.0', - 'glide': '4.14.2', - 'jsoup': '1.15.3', - 'httpClient': '4.5.13', - - 'androidxTestRunner': '1.4.0', - 'junit': '4.13.2', - 'robolectric': '4.9', - 'mockito': '4.8.0', - 'mockitoKotlin': '4.0.0', - 'truth': '1.1.3', - 'leakcanary': '2.9.1', - 'turbine': '0.11.0', - 'ktlint': '0.44.0' - ] - - javaVersion = JavaVersion.VERSION_11 - kotlinJvmVersion = "11" - } - - repositories { - mavenCentral() - google() - gradlePluginPortal() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" - classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.7.22-1.0.8" - classpath "org.jlleitschuh.gradle:ktlint-gradle:11.0.0" - } +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.android.lint) apply false + alias(libs.plugins.ksp) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.parcelize) apply false + alias(libs.plugins.kotlin.jvm) apply false + alias(libs.plugins.ktlint) apply false } project.ext { @@ -82,23 +19,23 @@ project.ext { allprojects { configurations.all { resolutionStrategy.dependencySubstitution { - substitute module("androidx.core:core") using module("androidx.core:core:${versions.androidxCore}") - substitute module("androidx.activity:activity") using module("androidx.activity:activity:${versions.androidxActivity}") - substitute module("androidx.activity:activity-ktx") using module("androidx.activity:activity-ktx:${versions.androidxActivity}") - substitute module("androidx.fragment:fragment") using module("androidx.fragment:fragment:${versions.androidxFragment}") - substitute module("androidx.fragment:fragment-ktx") using module("androidx.fragment:fragment-ktx:${versions.androidxFragment}") - substitute module("androidx.appcompat:appcompat") using module("androidx.appcompat:appcompat:${versions.androidxAppCompat}") - substitute module("androidx.preference:preference") using module("androidx.preference:preference:${versions.androidxPreference}") - substitute module("androidx.recyclerview:recyclerview") using module("androidx.recyclerview:recyclerview:${versions.androidxRecyclerView}") - substitute module("androidx.constraintlayout:constraintlayout") using module("androidx.constraintlayout:constraintlayout:${versions.androidxConstraintLayout}") - substitute module("androidx.drawerlayout:drawerlayout") using module("androidx.drawerlayout:drawerlayout:${versions.androidxDrawerLayout}") - substitute module("androidx.lifecycle:lifecycle-livedata") using module("androidx.lifecycle:lifecycle-livedata:${versions.androidxLifecycle}") - substitute module("androidx.transition:transition") using module("androidx.transition:transition:${versions.androidxTransition}") - substitute module("org.jetbrains:annotations") using module("org.jetbrains:annotations:${versions.jetbrainsAnnotations}") - substitute module("org.jetbrains.kotlin:kotlin-stdlib") using module("org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}") - substitute module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") using module("org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}") - substitute module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") using module("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}") - substitute module("org.jetbrains.kotlinx:kotlinx-coroutines-android") using module("org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlinCoroutines}") + substitute module("androidx.core:core") using module("androidx.core:core:${libs.versions.androidxCore.get()}") + substitute module("androidx.activity:activity") using module("androidx.activity:activity:${libs.versions.androidxActivity.get()}") + substitute module("androidx.activity:activity-ktx") using module("androidx.activity:activity-ktx:${libs.versions.androidxActivity.get()}") + substitute module("androidx.fragment:fragment") using module("androidx.fragment:fragment:${libs.versions.androidxFragment.get()}") + substitute module("androidx.fragment:fragment-ktx") using module("androidx.fragment:fragment-ktx:${libs.versions.androidxFragment.get()}") + substitute module("androidx.appcompat:appcompat") using module("androidx.appcompat:appcompat:${libs.versions.androidxAppCompat.get()}") + substitute module("androidx.preference:preference") using module("androidx.preference:preference:${libs.versions.androidxPreference.get()}") + substitute module("androidx.recyclerview:recyclerview") using module("androidx.recyclerview:recyclerview:${libs.versions.androidxRecyclerView.get()}") + substitute module("androidx.constraintlayout:constraintlayout") using module("androidx.constraintlayout:constraintlayout:${libs.versions.androidxConstraintLayout.get()}") + substitute module("androidx.drawerlayout:drawerlayout") using module("androidx.drawerlayout:drawerlayout:${libs.versions.androidxDrawerLayout.get()}") + substitute module("androidx.lifecycle:lifecycle-livedata") using module("androidx.lifecycle:lifecycle-livedata:${libs.versions.androidxLifecycle.get()}") + substitute module("androidx.transition:transition") using module("androidx.transition:transition:${libs.versions.androidxTransition.get()}") + substitute module("org.jetbrains:annotations") using module("org.jetbrains:annotations:${libs.versions.jetbrainsAnnotations.get()}") + substitute module("org.jetbrains.kotlin:kotlin-stdlib") using module("org.jetbrains.kotlin:kotlin-stdlib:${libs.versions.kotlin.get()}") + substitute module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") using module("org.jetbrains.kotlin:kotlin-stdlib-jdk7:${libs.versions.kotlin.get()}") + substitute module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") using module("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${libs.versions.kotlin.get()}") + substitute module("org.jetbrains.kotlinx:kotlinx-coroutines-android") using module("org.jetbrains.kotlinx:kotlinx-coroutines-android:${libs.versions.kotlinCoroutines.get()}") } } @@ -115,8 +52,8 @@ allprojects { } compileOptions { - sourceCompatibility javaVersion - targetCompatibility javaVersion + sourceCompatibility libs.versions.java.get() + targetCompatibility libs.versions.java.get() } lintOptions { @@ -134,8 +71,8 @@ allprojects { plugins.withType(JavaPlugin).configureEach { project.java { - sourceCompatibility = javaVersion - targetCompatibility = javaVersion + sourceCompatibility = libs.versions.java.get() + targetCompatibility = libs.versions.java.get() } } @@ -150,13 +87,13 @@ allprojects { tasks.withType(KotlinCompile) { kotlinOptions { - jvmTarget = javaVersion + jvmTarget = libs.versions.java.get() } } apply plugin: 'org.jlleitschuh.gradle.ktlint' ktlint { - version = versions.ktlint + version = libs.versions.ktlint.get() } } diff --git a/cli/html-cleaner-cli/build.gradle b/cli/html-cleaner-cli/build.gradle index 5d06b5f7ec3d75021ad7ad86fc59cf10e83cb474..26f2c9d593b934c9c8d8bcb8198391c0082e21cd 100644 --- a/cli/html-cleaner-cli/build.gradle +++ b/cli/html-cleaner-cli/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'org.jetbrains.kotlin.jvm' -apply plugin: 'application' +plugins { + alias(libs.plugins.kotlin.jvm) + id 'application' +} version 'unspecified' @@ -10,6 +12,6 @@ application { dependencies { implementation project(':app:html-cleaner') - implementation "com.github.ajalt.clikt:clikt:3.4.0" - implementation "com.squareup.okio:okio:${versions.okio}" + implementation libs.clikt + implementation libs.okio } diff --git a/fastlane/metadata/android/en-US/changelogs/35003.txt b/fastlane/metadata/android/en-US/changelogs/35003.txt new file mode 100644 index 0000000000000000000000000000000000000000..92056f363d9adc17c5c33f0b19db4bd4bed96132 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/35003.txt @@ -0,0 +1,5 @@ +- Tweaked theme colors +- Added support for HTML messages using the tag and relative links +- Fixed the logic to skip the trash folder when deleting messages, e.g. when deleting spam +- Fixed bug that could lead to messages not being marked as read on the server when they should have been +- Internal changes diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000000000000000000000000000000000000..2e444726871837b295f90eddd384e8dff146aba3 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,128 @@ +# Judging the impact of newer library versions on the app requires being intimately familiar with the code base. +# Please don't open pull requests upgrading dependencies if you're a new contributor. + +[versions] +java = "11" +androidGradlePlugin = "7.4.0" +ktlint = "0.44.0" + +kotlin = "1.8.0" +kotlinCoroutines = "1.6.4" +jetbrainsAnnotations = "24.0.0" +androidxAppCompat = "1.6.0" +androidxActivity = "1.6.1" +androidxRecyclerView = "1.2.1" +androidxLifecycle = "2.5.1" +androidxNavigation = "2.5.3" +androidxConstraintLayout = "2.1.4" +androidxFragment = "1.5.5" +androidxCore = "1.9.0" +androidxPreference = "1.2.0" +androidxDrawerLayout = "1.1.1" +androidxTransition = "1.4.1" +fastAdapter = "5.7.0" +preferencesFix = "1.1.0" +timber = "5.0.1" +koinCore = "3.3.2" +koinAndroid = "3.3.2" +mime4j = "0.8.8" +okhttp = "4.10.0" +glide = "4.14.2" +moshi = "1.14.0" +mockito = "5.0.0" + +[plugins] +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +android-lint = { id = "com.android.lint", version.ref = "androidGradlePlugin" } +ksp = "com.google.devtools.ksp:1.8.0-1.0.8" +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +ktlint = "org.jlleitschuh.gradle.ktlint:11.0.0" + +[libraries] +desugar = "com.android.tools:desugar_jdk_libs:1.1.8" + +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinCoroutines" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinCoroutines" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinCoroutines" } +jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppCompat" } +androidx-activity = { module = "androidx.activity:activity", version.ref = "androidxActivity" } +androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "androidxRecyclerView" } +androidx-lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "androidxLifecycle" } +androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" } +androidx-annotation = "androidx.annotation:annotation:1.5.0" +androidx-biometric = "androidx.biometric:biometric:1.1.0" +androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment", version.ref = "androidxNavigation" } +androidx-navigation-ui = { module = "androidx.navigation:navigation-ui", version.ref = "androidxNavigation" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintLayout" } +androidx-work-ktx = "androidx.work:work-runtime-ktx:2.7.1" +androidx-fragment = { module = "androidx.fragment:fragment", version.ref = "androidxFragment" } +androidx-localbroadcastmanager = "androidx.localbroadcastmanager:localbroadcastmanager:1.1.0" +androidx-core = { module = "androidx.core:core", version.ref = "androidxCore" } +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } +androidx-preference = { module = "androidx.preference:preference", version.ref = "androidxPreference" } +androidx-swiperefreshlayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" +androidx-test-core = "androidx.test:core:1.5.0" +android-material = "com.google.android.material:material:1.7.0" +fastadapter = { module = "com.mikepenz:fastadapter", version.ref = "fastAdapter" } +fastadapter-extensions-drag = { module = "com.mikepenz:fastadapter-extensions-drag", version.ref = "fastAdapter" } +fastadapter-extensions-utils = { module = "com.mikepenz:fastadapter-extensions-utils", version.ref = "fastAdapter" } +materialdrawer = "com.mikepenz:materialdrawer:8.4.5" +preferencex = { module = "com.takisoft.preferencex:preferencex", version.ref = "preferencesFix" } +preferencex-datetimepicker = { module = "com.takisoft.preferencex:preferencex-datetimepicker", version.ref = "preferencesFix" } +preferencex-colorpicker = { module = "com.takisoft.preferencex:preferencex-colorpicker", version.ref = "preferencesFix" } +okio = "com.squareup.okio:okio:3.3.0" +moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } +moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" } +timber = "com.jakewharton.timber:timber:5.0.1" +koin-core = { module = "io.insert-koin:koin-core", version.ref = "koinCore" } +koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinAndroid" } +koin-test = { module = "io.insert-koin:koin-test", version.ref = "koinCore" } +koin-test-junit4 = { module = "io.insert-koin:koin-test-junit4", version.ref = "koinCore" } +commons-io = "commons-io:commons-io:2.11.0" +mime4j-core = { module = "org.apache.james:apache-mime4j-core", version.ref = "mime4j" } +mime4j-dom = { module = "org.apache.james:apache-mime4j-dom", version.ref = "mime4j" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } +minidns-hla = "org.minidns:minidns-hla:1.0.4" +glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } +glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" } +jsoup = "org.jsoup:jsoup:1.15.3" +apache-httpclient = "org.apache.httpcomponents:httpclient:4.5.13" +apache-httpclient5 = "org.apache.httpcomponents.client5:httpclient5:5.1.3" +clikt = "com.github.ajalt.clikt:clikt:3.5.1" +jzlib = "com.jcraft:jzlib:1.0.7" +jutf7 = "com.beetstra.jutf7:jutf7:1.0.0" +jcip-annotations = "net.jcip:jcip-annotations:1.0" +jmap-client = "rs.ltt.jmap:jmap-client:0.3.1" +circleimageview = "de.hdodenhof:circleimageview:3.1.0" +appauth = "net.openid:appauth:0.11.1" +searchPreference = "com.github.ByteHamster:SearchPreference:v2.3.0" +safeContentResolver = "de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0" +tokenautocomplete = "com.splitwise:tokenautocomplete:4.0.0-beta01" +ckchangelog-core = "de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02" +xmlpull = "com.github.cketti:xmlpull-extracted-from-android:1.0" +kxml2 = "com.github.cketti:kxml2-extracted-from-android:1.0" +retrofit = "com.squareup.retrofit2:retrofit:2.9.0'" +retrofit-converter-simplexml = "com.squareup.retrofit2:converter-simplexml:2.9.0" +fullscreenloadingdialog = "com.github.fahim44:FullScreenLoadingDialog:1.0.7" +elib = "foundation.e:elib:0.0.1-alpha11" + +junit = "junit:junit:4.13.2" +robolectric = "org.robolectric:robolectric:4.9.2" +mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } +mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito" } +mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:4.1.0" +truth = "com.google.truth:truth:1.1.3" +turbine = "app.cash.turbine:turbine:0.12.1" +jdom2 = "org.jdom:jdom2:2.0.6.1" +icu4j-charset = "com.ibm.icu:icu4j-charset:72.1" + +leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.9.1" diff --git a/mail/common/build.gradle b/mail/common/build.gradle index 4681b3d0dd20c06e648c0fc69dbce7bd91ec7843..e6e6d57cb3b4a8b9de1d62b5b345cca58d3c4fa9 100644 --- a/mail/common/build.gradle +++ b/mail/common/build.gradle @@ -1,27 +1,29 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} if (rootProject.testCoverage) { apply plugin: 'jacoco' } dependencies { - api "org.jetbrains:annotations:${versions.jetbrainsAnnotations}" + api libs.jetbrains.annotations - implementation "org.apache.james:apache-mime4j-core:${versions.mime4j}" - implementation "org.apache.james:apache-mime4j-dom:${versions.mime4j}" - implementation "com.squareup.okio:okio:${versions.okio}" - implementation "commons-io:commons-io:${versions.commonsIo}" - implementation "com.squareup.moshi:moshi:${versions.moshi}" + implementation libs.mime4j.core + implementation libs.mime4j.dom + implementation libs.okio + implementation libs.commons.io + implementation libs.moshi // We're only using this for its DefaultHostnameVerifier - implementation "org.apache.httpcomponents.client5:httpclient5:5.1.3" + implementation libs.apache.httpclient5 testImplementation project(":mail:testing") - 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 "com.ibm.icu:icu4j-charset:70.1" + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.mockito.kotlin + testImplementation libs.icu4j.charset } diff --git a/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java b/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java index da138d2fa9c4f226b1d9a428d2676bed4fe1dc02..da697b12a064ca1901aa7d349fb4e0f1c04205bd 100644 --- a/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java +++ b/mail/common/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java @@ -149,9 +149,13 @@ public class MimeUtility { } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) { inputStream = new QuotedPrintableInputStream(rawInputStream) { @Override - public void close() throws IOException { + public void close() { super.close(); - closeInputStreamWithoutDeletingTemporaryFiles(rawInputStream); + try { + closeInputStreamWithoutDeletingTemporaryFiles(rawInputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } } }; } else { diff --git a/mail/protocols/imap/build.gradle b/mail/protocols/imap/build.gradle index ee26ab9a6bb9ea870c9eaf879a6028dd57caa0ea..eea587364ebc937cf257dc4f483a1438e303b08d 100644 --- a/mail/protocols/imap/build.gradle +++ b/mail/protocols/imap/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} if (rootProject.testCoverage) { apply plugin: 'jacoco' @@ -9,16 +11,16 @@ if (rootProject.testCoverage) { dependencies { api project(":mail:common") - implementation "com.jcraft:jzlib:1.0.7" - implementation "com.beetstra.jutf7:jutf7:1.0.0" - implementation "commons-io:commons-io:${versions.commonsIo}" - implementation "com.squareup.okio:okio:${versions.okio}" + implementation libs.jzlib + implementation libs.jutf7 + implementation libs.commons.io + implementation libs.okio testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "com.google.truth:truth:${versions.truth}" - testImplementation "org.mockito:mockito-core:${versions.mockito}" - testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}" - testImplementation "com.squareup.okio:okio:${versions.okio}" - testImplementation "org.apache.james:apache-mime4j-core:${versions.mime4j}" + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.core + testImplementation libs.mockito.kotlin + testImplementation libs.okio + testImplementation libs.mime4j.core } diff --git a/mail/protocols/pop3/build.gradle b/mail/protocols/pop3/build.gradle index 67ef9f6cded345cc165968e4f771d3c43eedd9ce..8ff2e742c3777e8d65580f184f4c4954e366114d 100644 --- a/mail/protocols/pop3/build.gradle +++ b/mail/protocols/pop3/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} if (rootProject.testCoverage) { apply plugin: 'jacoco' @@ -10,11 +12,11 @@ dependencies { api project(":mail:common") testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "com.google.truth:truth:${versions.truth}" - testImplementation "org.mockito:mockito-core:${versions.mockito}" - testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}" - testImplementation "com.squareup.okio:okio:${versions.okio}" - testImplementation "com.jcraft:jzlib:1.0.7" - testImplementation "commons-io:commons-io:${versions.commonsIo}" + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.core + testImplementation libs.mockito.kotlin + testImplementation libs.okio + testImplementation libs.jzlib + testImplementation libs.commons.io } diff --git a/mail/protocols/smtp/build.gradle b/mail/protocols/smtp/build.gradle index 8e8adb31238025416518b7ee9abc0fca8c82afbe..b4f19fab0fa0219c9570b03157d0aab523c619f1 100644 --- a/mail/protocols/smtp/build.gradle +++ b/mail/protocols/smtp/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} if (rootProject.testCoverage) { apply plugin: 'jacoco' @@ -9,13 +11,13 @@ if (rootProject.testCoverage) { dependencies { api project(":mail:common") - implementation "commons-io:commons-io:${versions.commonsIo}" - implementation "com.squareup.okio:okio:${versions.okio}" + implementation libs.commons.io + implementation libs.okio testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "com.google.truth:truth:${versions.truth}" - testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}" - testImplementation "com.squareup.okio:okio:${versions.okio}" - testImplementation "com.jcraft:jzlib:1.0.7" + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.kotlin + testImplementation libs.okio + testImplementation libs.jzlib } diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt index eb728642649c62b48d181d676b68f503a37fbd57..92c10aeb90bb4a855de56746ae57065746cbed5d 100644 --- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt +++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt @@ -283,8 +283,9 @@ class SmtpTransport( logResponse(smtpResponse) } - private fun logResponse(smtpResponse: SmtpResponse, omitText: Boolean = false) { + private fun logResponse(smtpResponse: SmtpResponse, sensitive: Boolean = false) { if (K9MailLib.isDebug()) { + val omitText = sensitive && !K9MailLib.isDebugSensitive() Timber.v("%s", smtpResponse.toLogString(omitText, linePrefix = "SMTP <<< ")) } } @@ -532,7 +533,7 @@ class SmtpTransport( repeat(pipelinedCommands.size) { val response = responseParser.readResponse(isEnhancedStatusCodesProvided) - logResponse(response, omitText = false) + logResponse(response) if (response.isNegativeResponse && firstException == null) { firstException = buildNegativeSmtpReplyException(response) diff --git a/mail/protocols/webdav/build.gradle b/mail/protocols/webdav/build.gradle index be69d3a646977d8d7b3026c0888f24dc200d3a1c..24eaada2dcf64d77e9c592c61cad1d85ea3850b0 100644 --- a/mail/protocols/webdav/build.gradle +++ b/mail/protocols/webdav/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'java-library' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + alias(libs.plugins.android.lint) +} if (rootProject.testCoverage) { apply plugin: 'jacoco' @@ -8,12 +10,12 @@ if (rootProject.testCoverage) { dependencies { api project(":mail:common") - implementation "commons-io:commons-io:${versions.commonsIo}" - compileOnly "org.apache.httpcomponents:httpclient:${versions.httpClient}" + implementation libs.commons.io + compileOnly libs.apache.httpclient testImplementation project(":mail:testing") - testImplementation "junit:junit:${versions.junit}" - testImplementation "com.google.truth:truth:${versions.truth}" - testImplementation "org.mockito:mockito-inline:${versions.mockito}" - testImplementation "org.apache.httpcomponents:httpclient:${versions.httpClient}" + testImplementation libs.junit + testImplementation libs.truth + testImplementation libs.mockito.inline + testImplementation libs.apache.httpclient } diff --git a/mail/testing/build.gradle b/mail/testing/build.gradle index 2c4e34f8a0ee57be3641d60797132b9e9a7d85fc..d09cf6891049991507b3b05778c31861af483365 100644 --- a/mail/testing/build.gradle +++ b/mail/testing/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'com.android.lint' +plugins { + id 'java-library' + id 'kotlin' + alias(libs.plugins.android.lint) +} if (rootProject.testCoverage) { apply plugin: 'jacoco' @@ -9,6 +11,6 @@ if (rootProject.testCoverage) { dependencies { api project(":mail:common") - api "com.squareup.okio:okio:${versions.okio}" - api "junit:junit:${versions.junit}" + api libs.okio + api libs.junit } diff --git a/plugins/openpgp-api-lib/openpgp-api/build.gradle b/plugins/openpgp-api-lib/openpgp-api/build.gradle index 7ba7c0b8fbba14231811ae9cc078e4a1db25f549..c0f8a0e184ea1fc73c258e9767c5dbbd926707b4 100644 --- a/plugins/openpgp-api-lib/openpgp-api/build.gradle +++ b/plugins/openpgp-api-lib/openpgp-api/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'com.android.library' +plugins { + alias(libs.plugins.android.library) +} android { namespace 'org.openintents.openpgp' @@ -9,7 +11,7 @@ android { } dependencies { - implementation "androidx.lifecycle:lifecycle-common:${versions.androidxLifecycle}" - implementation "com.jakewharton.timber:timber:${versions.timber}" - implementation "com.takisoft.preferencex:preferencex:${versions.preferencesFix}" + implementation libs.androidx.lifecycle.common + implementation libs.timber + implementation libs.preferencex } diff --git a/settings.gradle b/settings.gradle index 8068c735dec1a2510bc8f6d713119362fbb84c0c..dafd8a11c8eecc8135498a02e931e20c72273870 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,11 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { diff --git a/ui-utils/ItemTouchHelper/build.gradle b/ui-utils/ItemTouchHelper/build.gradle index 7c8d85a0d8b1f04a2ff1ea6bfe2968f0c440b364..b53ba41653420e96ce3dce72c8ceebffb7cfb296 100644 --- a/ui-utils/ItemTouchHelper/build.gradle +++ b/ui-utils/ItemTouchHelper/build.gradle @@ -1,7 +1,9 @@ -apply plugin: 'com.android.library' +plugins { + alias(libs.plugins.android.library) +} dependencies { - api "androidx.recyclerview:recyclerview:${versions.androidxRecyclerView}" + api libs.androidx.recyclerview } android { diff --git a/ui-utils/LinearLayoutManager/build.gradle b/ui-utils/LinearLayoutManager/build.gradle index 96cb5914700032acf52776e3ecc59fdde01e77c4..53ab0bd9a8729997d18d01b567d1e9f791029aca 100644 --- a/ui-utils/LinearLayoutManager/build.gradle +++ b/ui-utils/LinearLayoutManager/build.gradle @@ -1,7 +1,9 @@ -apply plugin: 'com.android.library' +plugins { + alias(libs.plugins.android.library) +} dependencies { - api "androidx.recyclerview:recyclerview:${versions.androidxRecyclerView}" + api libs.androidx.recyclerview } android {