diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a1eddc3db27c73fee7338e3e5b43a95e1ce9b785..40678861fa4ab18dbc55081a40ef52294c2152eb 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2,9 +2,9 @@
* @thunderbird/mobile-android-reviewers
# Release Engineering
-/.github/ @thunderbird/build-release
-/docs/ci/ @thunderbird/build-release
+/.github/ @thunderbird/build-release
+/docs/release/ @thunderbird/build-release
/scripts/ci/ @thunderbird/build-release
# CODEOWNERS protection
-/.github/CODEOWNERS @kewisch
+/.github/CODEOWNERS @Herbal7ea
diff --git a/.github/workflows/fluidscan.yml b/.github/workflows/fluidscan.yml
index da2511fa683b49468ccb98c6f23a59dae8d12dd4..1012ab38a44b0e704392fb8d6596661cffb67fb8 100644
--- a/.github/workflows/fluidscan.yml
+++ b/.github/workflows/fluidscan.yml
@@ -27,7 +27,7 @@ jobs:
bash scripts/ci/run-fluidattacks-scanner.sh
- name: "Upload scan results"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF scan results
path: fluidscan-results.sarif
@@ -35,6 +35,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
+ uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
with:
sarif_file: fluidscan-results.sarif
diff --git a/.github/workflows/pulls-opened.yml b/.github/workflows/pulls-opened.yml
index 225c80971a9a971fd99f46789a20dc2214dc0e9b..c9b2c6904b08eb85b1717fe4afe775139780e641 100644
--- a/.github/workflows/pulls-opened.yml
+++ b/.github/workflows/pulls-opened.yml
@@ -36,7 +36,8 @@ jobs:
Original Issue/Pull request:
Regression caused by (issue #):
User impact if declined:
- Testing completed (on daily, etc.):
+ Testing completed (on daily, beta, etc.):
+ Introduces or modifies localizable strings (yes/no):
Risk to taking this patch (and alternatives if risky):
run: |
if gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json comments \
diff --git a/.github/workflows/quality-codeql.yml b/.github/workflows/quality-codeql.yml
index a42934f8051b4d9e0d49685624033b8e9bc028cb..407d9c07f50ad7cb1663b068db2a4a5d0d262e63 100644
--- a/.github/workflows/quality-codeql.yml
+++ b/.github/workflows/quality-codeql.yml
@@ -43,7 +43,7 @@ jobs:
disable-cache: 'true'
- name: Initialize CodeQL
- uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
+ uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -53,6 +53,6 @@ jobs:
run: ./gradlew assemble --no-daemon
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
+ uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
with:
category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index f202cadd6aec7f83d7e6d7455a51285a80ffb962..aa7c0dac09baef82197eb26df66dae0ca135c5f3 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -54,7 +54,7 @@ jobs:
# Upload the results as artifacts.
- name: "Upload artifact"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF file
path: results.sarif
@@ -62,6 +62,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8
+ uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
with:
sarif_file: results.sarif
diff --git a/.github/workflows/shippable_builds.yml b/.github/workflows/shippable_builds.yml
index 27790f89162b60acf761df448146aa4511c450f6..dd0c33f512015b0e103dbf2062f1dcc8c37e2f7e 100644
--- a/.github/workflows/shippable_builds.yml
+++ b/.github/workflows/shippable_builds.yml
@@ -543,7 +543,7 @@ jobs:
ls -l ${UPLOAD_PATH}/
- name: Upload unsigned
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
env:
UPLOAD_PATH: "uploads"
with:
@@ -563,7 +563,7 @@ jobs:
env:
RELEASE_TYPE: ${{ needs.dump_config.outputs.releaseType }}
steps:
- - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: unsigned-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
path: uploads/
@@ -592,7 +592,7 @@ jobs:
rm -f uploads/*.jks
- name: Upload signed
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: signed-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
if-no-files-found: error
@@ -684,7 +684,7 @@ jobs:
ref: ${{ steps.shanotes.outputs.app_sha }}
- name: Download Artifacts
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: signed-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
path: "uploads/"
@@ -755,7 +755,7 @@ jobs:
- name: Publish to GitHub Releases
id: publish_gh
if: ${{ contains(matrix.releaseTarget, 'github') }}
- uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
+ uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
with:
token: ${{ steps.app-token.outputs.token || github.token }}
target_commitish: ${{ steps.shanotes.outputs.app_sha }}
diff --git a/.gitignore b/.gitignore
index 8fe66b93a4aa97c02de013e6436e41d8b93cc770..467acf470d9a506d6fe3d56c23384136a0edb494 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,5 @@ captures/
# Screenshots
adb-screenshots/
+# Extracted eml files
+eml-files/
diff --git a/README.md b/README.md
index 3d1d2109e871ccd46b90f4f98a26b7a291b39845..9c95d309fda7e9a95c7a5eb83e29aef7e051f8b8 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
[](https://github.com/thunderbird/thunderbird-android/releases/latest)
[](https://github.com/thunderbird/thunderbird-android/releases)
@@ -18,7 +18,7 @@ Thunderbird for Android can be downloaded from a couple of sources:
- Thunderbird Beta on [Google Play](https://play.google.com/store/apps/details?id=net.thunderbird.android.beta&referrer=utm_campaign%3Dandroid_metadata%26utm_medium%3Dweb%26utm_source%3Dgithub.com%26utm_content%3Dlink) or [F-Droid](https://f-droid.org/packages/net.thunderbird.android.beta)
- [Github Releases](https://github.com/thunderbird/thunderbird-android/releases)
- [FFUpdater](https://f-droid.org/packages/de.marmaro.krt.ffupdater/) allows installing the latest versions from ftp.mozilla.org
-- [Obtanium](https://obtainium.imranr.dev/) requires app-specific configuration from the [Complex Obtanium Apps list](https://apps.obtainium.imranr.dev/)
+- [Obtainium](https://obtainium.imranr.dev/) requires app-specific configuration from the [Complex Obtainium Apps list](https://apps.obtainium.imranr.dev/)
By using Thunderbird for Android Beta, you have early access to current development and are able to try new features earlier.
diff --git a/app-common/build.gradle.kts b/app-common/build.gradle.kts
index 4bcba76fb7c1dc7d59737feb130e6e27f16bb91c..373e8e396a0c9b0c8dee7757ba3ac634635314bc 100644
--- a/app-common/build.gradle.kts
+++ b/app-common/build.gradle.kts
@@ -31,17 +31,25 @@ dependencies {
implementation(projects.core.configstore.implBackend)
implementation(projects.core.featureflag)
+ implementation(projects.core.file)
+
+ implementation(projects.core.ui.setting.api)
+ implementation(projects.core.ui.setting.implDialog)
implementation(projects.core.ui.legacy.theme2.common)
implementation(projects.feature.account.avatar.api)
implementation(projects.feature.account.avatar.impl)
implementation(projects.feature.account.setup)
implementation(projects.feature.mail.account.api)
+ implementation(projects.feature.mail.message.composer)
implementation(projects.feature.migration.provider)
implementation(projects.feature.notification.api)
implementation(projects.feature.notification.impl)
implementation(projects.feature.widget.messageList)
+ implementation(projects.feature.mail.message.export.api)
+ implementation(projects.feature.mail.message.export.implEml)
+
implementation(projects.mail.protocols.imap)
implementation(projects.backend.imap)
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/core/AppCommonCoreModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/core/AppCommonCoreModule.kt
index a1d7430af48a50eedba2da230ebc798c0cb98551..cfe25354936acca69e5c4734b7125374b5977c9b 100644
--- a/app-common/src/main/kotlin/net/thunderbird/app/common/core/AppCommonCoreModule.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/core/AppCommonCoreModule.kt
@@ -1,7 +1,13 @@
package net.thunderbird.app.common.core
+import android.content.Context
import net.thunderbird.app.common.core.configstore.appCommonCoreConfigStoreModule
import net.thunderbird.app.common.core.logging.appCommonCoreLogger
+import net.thunderbird.app.common.core.ui.appCommonCoreUiModule
+import net.thunderbird.core.file.AndroidFileSystemManager
+import net.thunderbird.core.file.DefaultFileManager
+import net.thunderbird.core.file.FileManager
+import net.thunderbird.core.file.FileSystemManager
import org.koin.core.module.Module
import org.koin.dsl.module
@@ -9,5 +15,18 @@ val appCommonCoreModule: Module = module {
includes(
appCommonCoreConfigStoreModule,
appCommonCoreLogger,
+ appCommonCoreUiModule,
)
+
+ single {
+ AndroidFileSystemManager(
+ contentResolver = get().contentResolver,
+ )
+ }
+
+ single {
+ DefaultFileManager(
+ fileSystemManager = get(),
+ )
+ }
}
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/core/logging/LoggerModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/core/logging/LoggerModule.kt
index e31b9f2c0b95d9edbe686bffa610375659138379..3bb495a636c1067391c2fec932402130e4ebe8bd 100644
--- a/app-common/src/main/kotlin/net/thunderbird/app/common/core/logging/LoggerModule.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/core/logging/LoggerModule.kt
@@ -13,7 +13,6 @@ import net.thunderbird.core.logging.LogSink
import net.thunderbird.core.logging.Logger
import net.thunderbird.core.logging.composite.CompositeLogSink
import net.thunderbird.core.logging.console.ConsoleLogSink
-import net.thunderbird.core.logging.file.AndroidFileSystemManager
import net.thunderbird.core.logging.file.FileLogSink
import org.koin.core.qualifier.named
import org.koin.dsl.bind
@@ -46,10 +45,16 @@ val appCommonCoreLogger = module {
)
}
+ // Setup for sync debug logger
+ // Define this list lazily to avoid eager initialization at app startup
+ single>(qualifier = named(SYNC_DEBUG_LOG), createdAtStart = false) {
+ listOf(get(named(SYNC_DEBUG_LOG)))
+ }
+
single(named(SYNC_DEBUG_LOG)) {
CompositeLogSink(
logLevelProvider = get(),
- sinks = getList(),
+ sinks = get>(named(SYNC_DEBUG_LOG)),
)
}
@@ -58,7 +63,7 @@ val appCommonCoreLogger = module {
level = LogLevel.DEBUG,
fileName = "thunderbird-sync-debug",
fileLocation = get().filesDir.path,
- fileSystemManager = AndroidFileSystemManager(get().contentResolver),
+ fileManager = get(),
)
}
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/core/ui/AppCommonCoreUiModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/core/ui/AppCommonCoreUiModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b60b28c1ecd7692387adcf09310a60f339cf4d36
--- /dev/null
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/core/ui/AppCommonCoreUiModule.kt
@@ -0,0 +1,9 @@
+package net.thunderbird.app.common.core.ui
+
+import net.thunderbird.core.ui.setting.SettingViewProvider
+import net.thunderbird.core.ui.setting.dialog.DialogSettingViewProvider
+import org.koin.dsl.module
+
+val appCommonCoreUiModule = module {
+ single { DialogSettingViewProvider() }
+}
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt
index f43927d52155fa830f6d4eade7479f7c8fbee616..3d8411b57ff72bb6daacd15daa140d91f53afa88 100644
--- a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/AppCommonFeatureModule.kt
@@ -3,6 +3,7 @@ package net.thunderbird.app.common.feature
import app.k9mail.feature.launcher.FeatureLauncherExternalContract
import app.k9mail.feature.launcher.di.featureLauncherModule
import net.thunderbird.app.common.feature.mail.appCommonFeatureMailModule
+import net.thunderbird.feature.mail.message.composer.inject.featureMessageComposerModule
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract
import net.thunderbird.feature.notification.impl.inject.featureNotificationModule
import org.koin.android.ext.koin.androidContext
@@ -11,6 +12,7 @@ import org.koin.dsl.module
internal val appCommonFeatureModule = module {
includes(featureLauncherModule)
includes(featureNotificationModule)
+ includes(featureMessageComposerModule)
includes(appCommonFeatureMailModule)
factory {
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt
index 5ccb4b8b5b9702349d0a8c46415dd9126dbaab5a..c6ee5c3a1f9fdd78bb9029bddd710f2711f5e5d9 100644
--- a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/FeatureMailModule.kt
@@ -2,6 +2,7 @@ package net.thunderbird.app.common.feature.mail
import com.fsck.k9.mailstore.DefaultSpecialFolderUpdater
import com.fsck.k9.mailstore.LegacyAccountDtoSpecialFolderUpdaterFactory
+import net.thunderbird.app.common.feature.mail.message.mailMessageModule
import net.thunderbird.backend.api.BackendFactory
import net.thunderbird.backend.api.BackendStorageFactory
import net.thunderbird.backend.api.folder.RemoteFolderCreator
@@ -13,6 +14,8 @@ import org.koin.dsl.module
internal val appCommonFeatureMailModule = module {
+ includes(mailMessageModule)
+
single> {
BaseAccountBackendStorageFactory(
legacyFactory = get(),
diff --git a/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/message/MailMessageModule.kt b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/message/MailMessageModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d25b1eb3d33b896aee8d1c27b46e72e5c1fd7efe
--- /dev/null
+++ b/app-common/src/main/kotlin/net/thunderbird/app/common/feature/mail/message/MailMessageModule.kt
@@ -0,0 +1,17 @@
+package net.thunderbird.app.common.feature.mail.message
+
+import net.thunderbird.feature.mail.message.export.DefaultMessageFileNameSuggester
+import net.thunderbird.feature.mail.message.export.MessageExporter
+import net.thunderbird.feature.mail.message.export.MessageFileNameSuggester
+import net.thunderbird.feature.mail.message.export.eml.EmlMessageExporter
+import org.koin.dsl.module
+
+internal val mailMessageModule = module {
+ single { DefaultMessageFileNameSuggester() }
+
+ single {
+ EmlMessageExporter(
+ fileManager = get(),
+ )
+ }
+}
diff --git a/app-common/src/main/res/values-am/strings.xml b/app-common/src/main/res/values-am/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-am/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ar/strings.xml b/app-common/src/main/res/values-ar/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ar/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ast/strings.xml b/app-common/src/main/res/values-ast/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ast/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-az/strings.xml b/app-common/src/main/res/values-az/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-az/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-be/strings.xml b/app-common/src/main/res/values-be/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-be/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-bg/strings.xml b/app-common/src/main/res/values-bg/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-bg/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-bn/strings.xml b/app-common/src/main/res/values-bn/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-bn/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-br/strings.xml b/app-common/src/main/res/values-br/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-br/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-bs/strings.xml b/app-common/src/main/res/values-bs/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-bs/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ca/strings.xml b/app-common/src/main/res/values-ca/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ca/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-co/strings.xml b/app-common/src/main/res/values-co/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-co/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-cs/strings.xml b/app-common/src/main/res/values-cs/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-cs/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-cy/strings.xml b/app-common/src/main/res/values-cy/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-cy/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-da/strings.xml b/app-common/src/main/res/values-da/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-da/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-de/strings.xml b/app-common/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-de/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-el/strings.xml b/app-common/src/main/res/values-el/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-el/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-en-rGB/strings.xml b/app-common/src/main/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-en-rGB/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-enm/strings.xml b/app-common/src/main/res/values-enm/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-enm/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-eo/strings.xml b/app-common/src/main/res/values-eo/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-eo/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-es/strings.xml b/app-common/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-es/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-et/strings.xml b/app-common/src/main/res/values-et/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-et/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-eu/strings.xml b/app-common/src/main/res/values-eu/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-eu/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-fa/strings.xml b/app-common/src/main/res/values-fa/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-fa/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-fi/strings.xml b/app-common/src/main/res/values-fi/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-fi/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-fr/strings.xml b/app-common/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-fr/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-fy/strings.xml b/app-common/src/main/res/values-fy/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-fy/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ga/strings.xml b/app-common/src/main/res/values-ga/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ga/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-gd/strings.xml b/app-common/src/main/res/values-gd/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-gd/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-gl/strings.xml b/app-common/src/main/res/values-gl/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-gl/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-gu/strings.xml b/app-common/src/main/res/values-gu/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-gu/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-hi/strings.xml b/app-common/src/main/res/values-hi/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-hi/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-hr/strings.xml b/app-common/src/main/res/values-hr/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-hr/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ht/strings.xml b/app-common/src/main/res/values-ht/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ht/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-hu/strings.xml b/app-common/src/main/res/values-hu/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-hu/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-hy/strings.xml b/app-common/src/main/res/values-hy/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-hy/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-in/strings.xml b/app-common/src/main/res/values-in/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-in/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-is/strings.xml b/app-common/src/main/res/values-is/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-is/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-it/strings.xml b/app-common/src/main/res/values-it/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-it/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-iw/strings.xml b/app-common/src/main/res/values-iw/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-iw/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ja/strings.xml b/app-common/src/main/res/values-ja/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ja/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ka/strings.xml b/app-common/src/main/res/values-ka/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ka/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-kab/strings.xml b/app-common/src/main/res/values-kab/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-kab/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-kk/strings.xml b/app-common/src/main/res/values-kk/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-kk/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-kn/strings.xml b/app-common/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-kn/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ko/strings.xml b/app-common/src/main/res/values-ko/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ko/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-lt/strings.xml b/app-common/src/main/res/values-lt/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-lt/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-lv/strings.xml b/app-common/src/main/res/values-lv/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-lv/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ml/strings.xml b/app-common/src/main/res/values-ml/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ml/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-mnw/strings.xml b/app-common/src/main/res/values-mnw/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-mnw/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-nb-rNO/strings.xml b/app-common/src/main/res/values-nb-rNO/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-nb-rNO/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-nl/strings.xml b/app-common/src/main/res/values-nl/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-nl/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-nn/strings.xml b/app-common/src/main/res/values-nn/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-nn/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-pl/strings.xml b/app-common/src/main/res/values-pl/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-pl/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-pt-rBR/strings.xml b/app-common/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-pt-rPT/strings.xml b/app-common/src/main/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-pt-rPT/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-pt/strings.xml b/app-common/src/main/res/values-pt/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-pt/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ro/strings.xml b/app-common/src/main/res/values-ro/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ro/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ru/strings.xml b/app-common/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ru/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-sk/strings.xml b/app-common/src/main/res/values-sk/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-sk/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-sl/strings.xml b/app-common/src/main/res/values-sl/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-sl/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-sq/strings.xml b/app-common/src/main/res/values-sq/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-sq/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-sr/strings.xml b/app-common/src/main/res/values-sr/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-sr/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-sv/strings.xml b/app-common/src/main/res/values-sv/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-sv/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-sw/strings.xml b/app-common/src/main/res/values-sw/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-sw/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-ta/strings.xml b/app-common/src/main/res/values-ta/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-ta/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-th/strings.xml b/app-common/src/main/res/values-th/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-th/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-tr/strings.xml b/app-common/src/main/res/values-tr/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-tr/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-uk/strings.xml b/app-common/src/main/res/values-uk/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-uk/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-vi/strings.xml b/app-common/src/main/res/values-vi/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-vi/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-zh-rCN/strings.xml b/app-common/src/main/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-zh-rCN/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-common/src/main/res/values-zh-rTW/strings.xml b/app-common/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-common/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-k9mail/badging/fossRelease-badging.txt b/app-k9mail/badging/fossRelease-badging.txt
index 256b1fbce6c26316e69f03def5cd6d90fbe5e222..9bd213c40008bd6a23699d2cd016e471d42579f1 100644
--- a/app-k9mail/badging/fossRelease-badging.txt
+++ b/app-k9mail/badging/fossRelease-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='com.fsck.k9' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='com.fsck.k9' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-k9mail/badging/fullRelease-badging.txt b/app-k9mail/badging/fullRelease-badging.txt
index 5a5c447d6190318b63c693d93f6b5f2f55c4dd6a..3f1917df6f47db09404a1c28c0b2abcceb260ea6 100644
--- a/app-k9mail/badging/fullRelease-badging.txt
+++ b/app-k9mail/badging/fullRelease-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='com.fsck.k9' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='com.fsck.k9' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-k9mail/build.gradle.kts b/app-k9mail/build.gradle.kts
index a88dde97156aa66dc6f86b8823550b6020228d16..4c34f726d458a478fd961e34fbf03a903edf2169 100644
--- a/app-k9mail/build.gradle.kts
+++ b/app-k9mail/build.gradle.kts
@@ -18,7 +18,7 @@ android {
testApplicationId = "com.fsck.k9.tests"
versionCode = 39021
- versionName = "14.0"
+ versionName = "15.0"
versionNameSuffix = "b1"
buildConfigField("String", "CLIENT_INFO_APP_NAME", "\"K-9 Mail\"")
diff --git a/app-k9mail/dependencies/fossReleaseRuntimeClasspath.txt b/app-k9mail/dependencies/fossReleaseRuntimeClasspath.txt
index fd3ba8c643ac118858cd69e505158db8dab3182c..4b169cff440746dfd3814a66e25bf2583d4bfb2a 100644
--- a/app-k9mail/dependencies/fossReleaseRuntimeClasspath.txt
+++ b/app-k9mail/dependencies/fossReleaseRuntimeClasspath.txt
@@ -37,12 +37,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -63,11 +63,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -100,28 +100,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -140,11 +140,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -172,15 +172,17 @@ co.touchlab:stately-concurrent-collections:2.1.0
co.touchlab:stately-strict-jvm:2.1.0
co.touchlab:stately-strict:2.1.0
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.flexbox:flexbox:3.0.0
com.google.android.material:material:1.12.0
com.google.errorprone:error_prone_annotations:2.15.0
@@ -193,10 +195,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -206,16 +208,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -236,14 +238,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -255,7 +257,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -263,12 +265,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-k9mail/dependencies/fullReleaseRuntimeClasspath.txt b/app-k9mail/dependencies/fullReleaseRuntimeClasspath.txt
index de8d7e2c9c38af03533ec56341c492b7c3c5a680..a82688e3165cdc3c5dd0f0175da4763994023de5 100644
--- a/app-k9mail/dependencies/fullReleaseRuntimeClasspath.txt
+++ b/app-k9mail/dependencies/fullReleaseRuntimeClasspath.txt
@@ -37,12 +37,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -63,11 +63,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -100,28 +100,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -140,11 +140,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -174,15 +174,17 @@ co.touchlab:stately-strict:2.1.0
com.android.billingclient:billing-ktx:7.1.1
com.android.billingclient:billing:7.1.1
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.datatransport:transport-api:3.0.0
com.google.android.datatransport:transport-backend-cct:3.1.8
com.google.android.datatransport:transport-runtime:3.1.8
@@ -206,10 +208,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -219,16 +221,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -250,14 +252,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -269,7 +271,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -277,12 +279,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-k9mail/src/debug/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt b/app-k9mail/src/debug/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt
index c9c81aae4ed35779fa55b88996d1c72c9a6e2120..433ae8ba14bd2fc115f7259a6279e6fea74d690c 100644
--- a/app-k9mail/src/debug/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt
+++ b/app-k9mail/src/debug/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt
@@ -1,10 +1,12 @@
package app.k9mail.featureflag
import com.fsck.k9.ui.messagelist.MessageListFeatureFlags
+import com.fsck.k9.ui.messageview.MessageViewFeatureFlags
import net.thunderbird.core.featureflag.FeatureFlag
import net.thunderbird.core.featureflag.FeatureFlagFactory
import net.thunderbird.core.featureflag.FeatureFlagKey
import net.thunderbird.core.featureflag.toFeatureFlagKey
+import net.thunderbird.feature.account.settings.AccountSettingsFeatureFlags
class K9FeatureFlagFactory : FeatureFlagFactory {
override fun createFeatureCatalog(): List {
@@ -18,6 +20,8 @@ class K9FeatureFlagFactory : FeatureFlagFactory {
FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = false),
FeatureFlag(FeatureFlagKey.UseNotificationSenderForSystemNotifications, enabled = false),
FeatureFlag(MessageListFeatureFlags.UseComposeForMessageListItems, enabled = false),
+ FeatureFlag(MessageViewFeatureFlags.ActionExportEml, enabled = true),
+ FeatureFlag(AccountSettingsFeatureFlags.EnableAvatarCustomization, enabled = false),
)
}
}
diff --git a/app-k9mail/src/main/kotlin/app/k9mail/provider/K9AppNotificationIconProvider.kt b/app-k9mail/src/main/kotlin/app/k9mail/provider/K9AppNotificationIconProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6d53a01af18f376caac2b8a18a7ed3c13449f699
--- /dev/null
+++ b/app-k9mail/src/main/kotlin/app/k9mail/provider/K9AppNotificationIconProvider.kt
@@ -0,0 +1,10 @@
+package app.k9mail.provider
+
+import app.k9mail.core.android.common.provider.NotificationIconResourceProvider
+import app.k9mail.core.ui.legacy.theme2.k9mail.R
+
+class K9AppNotificationIconProvider : NotificationIconResourceProvider {
+ override val pushNotificationIcon: Int
+
+ get() = R.drawable.ic_logo_k9_white
+}
diff --git a/app-k9mail/src/main/kotlin/app/k9mail/provider/ProviderModule.kt b/app-k9mail/src/main/kotlin/app/k9mail/provider/ProviderModule.kt
index 07e236989f7277b1ae5782f49445621d1c174da6..303aca7908bede05c99b5b963bff36e900d9807a 100644
--- a/app-k9mail/src/main/kotlin/app/k9mail/provider/ProviderModule.kt
+++ b/app-k9mail/src/main/kotlin/app/k9mail/provider/ProviderModule.kt
@@ -1,5 +1,6 @@
package app.k9mail.provider
+import app.k9mail.core.android.common.provider.NotificationIconResourceProvider
import com.fsck.k9.preferences.FilePrefixProvider
import net.thunderbird.core.common.provider.AppNameProvider
import net.thunderbird.core.common.provider.BrandNameProvider
@@ -17,4 +18,8 @@ internal val providerModule = module {
single { K9ThemeProvider() }
single { K9FeatureThemeProvider() }
+
+ single {
+ K9AppNotificationIconProvider()
+ }
}
diff --git a/app-k9mail/src/main/res/values-ht/strings.xml b/app-k9mail/src/main/res/values-ht/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-k9mail/src/main/res/values-ht/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-k9mail/src/main/res/values-kn/strings.xml b/app-k9mail/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-k9mail/src/main/res/values-kn/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-k9mail/src/main/res/values-mnw/strings.xml b/app-k9mail/src/main/res/values-mnw/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a29a9fcf41394188c44e093f470c62cf9ed2d5a2
--- /dev/null
+++ b/app-k9mail/src/main/res/values-mnw/strings.xml
@@ -0,0 +1,4 @@
+
+
+ တိုက်လိက် ခေ-၉
+
diff --git a/app-k9mail/src/release/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt b/app-k9mail/src/release/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt
index 22ce98a94aa2a48ab7d6cfec24c85d199ee048c4..e8058409a7600e67647e083ce70cc60389e60d74 100644
--- a/app-k9mail/src/release/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt
+++ b/app-k9mail/src/release/kotlin/app/k9mail/featureflag/K9FeatureFlagFactory.kt
@@ -1,15 +1,16 @@
package app.k9mail.featureflag
import com.fsck.k9.ui.messagelist.MessageListFeatureFlags
+import com.fsck.k9.ui.messageview.MessageViewFeatureFlags
import net.thunderbird.core.featureflag.FeatureFlag
import net.thunderbird.core.featureflag.FeatureFlagFactory
import net.thunderbird.core.featureflag.FeatureFlagKey
import net.thunderbird.core.featureflag.toFeatureFlagKey
+import net.thunderbird.feature.account.settings.AccountSettingsFeatureFlags
/**
* Feature flags for K-9 Mail (release)
*/
-
class K9FeatureFlagFactory : FeatureFlagFactory {
override fun createFeatureCatalog(): List {
return listOf(
@@ -22,6 +23,8 @@ class K9FeatureFlagFactory : FeatureFlagFactory {
FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = false),
FeatureFlag(FeatureFlagKey.UseNotificationSenderForSystemNotifications, enabled = false),
FeatureFlag(MessageListFeatureFlags.UseComposeForMessageListItems, enabled = false),
+ FeatureFlag(MessageViewFeatureFlags.ActionExportEml, enabled = false),
+ FeatureFlag(AccountSettingsFeatureFlags.EnableAvatarCustomization, enabled = false),
)
}
}
diff --git a/app-k9mail/src/test/kotlin/app/k9mail/K9AppNotificationIconProviderTest.kt b/app-k9mail/src/test/kotlin/app/k9mail/K9AppNotificationIconProviderTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ab59d1077bb28d3ac6604248058af75d4c5b7ffb
--- /dev/null
+++ b/app-k9mail/src/test/kotlin/app/k9mail/K9AppNotificationIconProviderTest.kt
@@ -0,0 +1,17 @@
+package app.k9mail
+
+import app.k9mail.provider.K9AppNotificationIconProvider
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import kotlin.test.Test
+
+class K9AppNotificationIconProviderTest {
+ @Test
+ fun `provides correct K9 notification icon`() {
+ val provider = K9AppNotificationIconProvider()
+ val icon = provider.pushNotificationIcon
+
+ assertThat(icon)
+ .isEqualTo(app.k9mail.core.ui.legacy.theme2.k9mail.R.drawable.ic_logo_k9_white)
+ }
+}
diff --git a/app-metadata/com.fsck.k9/eo/title.txt b/app-metadata/com.fsck.k9/eo/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..680b102e340ed933acb961205a382a958c24a064
--- /dev/null
+++ b/app-metadata/com.fsck.k9/eo/title.txt
@@ -0,0 +1 @@
+K-9 Retpoŝtilo
diff --git a/app-metadata/com.fsck.k9/mnw/title.txt b/app-metadata/com.fsck.k9/mnw/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..30118793d75a86b87ab46c1a8f83f08ca1b57067
--- /dev/null
+++ b/app-metadata/com.fsck.k9/mnw/title.txt
@@ -0,0 +1 @@
+တိုက်လိက် K-၉
diff --git a/app-metadata/com.fsck.k9/nn/full_description.txt b/app-metadata/com.fsck.k9/nn/full_description.txt
index 056a77367ce40bbcea50e07dd58e804a87e1c357..21d470c486f4edc953b200af2e5266b190156c29 100644
--- a/app-metadata/com.fsck.k9/nn/full_description.txt
+++ b/app-metadata/com.fsck.k9/nn/full_description.txt
@@ -6,15 +6,15 @@ K-9 Mail er ein e-post klient med 100% open kjeldekode, som verker med omtrent k
* Samla innboks
* Personvern-vennleg (ingen sporing i det heile, kontaktar kun e-posttilbydaren din)
* Automatisk bakkgrunnssynkronisering eller dyttenotifikasjonar
-* Lokal og tenar-side søk
+* Søk lokalt og på tenaren
* OpenPGP e-post kryptering (PGP/MIME)
Installer appen OpenKeychain: Easy PGP for å enkryptere/dekryptere e-postane dine med hjelp av OpenPGP.
-Kundestønad
+Støtte
-Om du har problem med K-9 Mail, spør om hjelp på vårt kundestønadsforum.
+Om du har problemar med K-9 Mail, kan du spørje om hjelp på vårt støtteforum.
Vil du hjelpa?
diff --git a/app-metadata/net.thunderbird.android.beta/eo/short_description.txt b/app-metadata/net.thunderbird.android.beta/eo/short_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..61c36826a2a18ba2dbf6e6d09cbee362c3aff819
--- /dev/null
+++ b/app-metadata/net.thunderbird.android.beta/eo/short_description.txt
@@ -0,0 +1 @@
+Thunderbird estas plene malfermitkoda, privateco-gardanta retpoŝtilo.
diff --git a/app-metadata/net.thunderbird.android.beta/eo/title.txt b/app-metadata/net.thunderbird.android.beta/eo/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2929797d77155da0b0863e0da957fc5f15fe6215
--- /dev/null
+++ b/app-metadata/net.thunderbird.android.beta/eo/title.txt
@@ -0,0 +1 @@
+Thunderbird Beta por Testantoj
diff --git a/app-metadata/net.thunderbird.android.beta/hi-IN/short_description.txt b/app-metadata/net.thunderbird.android.beta/hi-IN/short_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5f81c3818d6ce387b7b5c4f52e7d4539826d7449
--- /dev/null
+++ b/app-metadata/net.thunderbird.android.beta/hi-IN/short_description.txt
@@ -0,0 +1 @@
+थंडरबर्ड एक 100% खुला स्रोत, गोपनीयता केंद्रित ईमेल ऐप है।
diff --git a/app-metadata/net.thunderbird.android.beta/hi-IN/title.txt b/app-metadata/net.thunderbird.android.beta/hi-IN/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f14db2a509aef2af2bf1e963857093acce5c143a
--- /dev/null
+++ b/app-metadata/net.thunderbird.android.beta/hi-IN/title.txt
@@ -0,0 +1 @@
+परीक्षकों के लिये थंडरबर्ड बीटा
diff --git a/app-metadata/net.thunderbird.android.beta/is-IS/title.txt b/app-metadata/net.thunderbird.android.beta/is-IS/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..86c491a80106ace1cb874f6507966bdeaa938d96
--- /dev/null
+++ b/app-metadata/net.thunderbird.android.beta/is-IS/title.txt
@@ -0,0 +1 @@
+Thunderbird Beta til prófana
diff --git a/app-metadata/net.thunderbird.android.beta/mnw/title.txt b/app-metadata/net.thunderbird.android.beta/mnw/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f5d270823f316725c67656086ec1a60c95048ba8
--- /dev/null
+++ b/app-metadata/net.thunderbird.android.beta/mnw/title.txt
@@ -0,0 +1 @@
+Thunderbird Beta သွက်ရဲစမ်ၜတ်
diff --git a/app-metadata/net.thunderbird.android.beta/pt-BR/full_description.txt b/app-metadata/net.thunderbird.android.beta/pt-BR/full_description.txt
index c503983a74a8a8fd6af74d564047243fbe3bf27f..785c48374301fbc475253712327ee6a69474ab17 100644
--- a/app-metadata/net.thunderbird.android.beta/pt-BR/full_description.txt
+++ b/app-metadata/net.thunderbird.android.beta/pt-BR/full_description.txt
@@ -1,43 +1,25 @@
-Ajude a tornar a próxima versão do Thunderbird a melhor possível baixando o Thunderbird Beta e tendo acesso antecipado aos recursos mais recentes e correções de bugs antes do lançamento oficial. Seus testes e feedback são importantes, então, por favor, reporte bugs, problemas e compartilhe suas opiniões conosco!
+Ajude a tornar a próxima versão do Thunderbird a melhor possível baixando o Thunderbird Beta e tendo acesso antecipado aos recursos mais recentes e correções de bugs antes do lançamento oficial. Seus testes e feedback são importantes, então por favor reporte bugs, problemas e compartilhe suas opiniões conosco!
-Encontre nosso rastreador de bugs, código-fonte e wiki em https://github.com/thunderbird/thunderbird-android.
+Encontre nosso rastreador de bugs, código-fonte e wiki em https://github.com/thunderbird/thunderbird-android
+.
-Estamos sempre felizes em receber novos desenvolvedores, designers, documentadores, tradutores, revisores de bugs e amigos. Visite https://thunderbird.net/participate para começar.
+Estamos sempre felizes em receber novos desenvolvedores, designers, redatores de documentação, tradutores, triadores de bugs e amigos. Visite https://thunderbird.net/participate
+ para começar.
-O Thunderbird é um poderoso aplicativo de e-mail focado em privacidade. Gerencie facilmente várias contas de e-mail em um único aplicativo, com a opção de Caixa de Entrada Unificada para máxima produtividade. Construído com tecnologia de código aberto e apoiado por uma equipe dedicada de desenvolvedores, junto a uma comunidade global de voluntários, o Thunderbird nunca trata seus dados privados como produto. É financiado exclusivamente por contribuições dos usuários, então você nunca verá anúncios misturados aos seus e-mails.
+O Thunderbird é um poderoso aplicativo de e-mail focado em privacidade. Gerencie múltiplas contas de e-mail em um único app, com a opção de Caixa de Entrada Unificada para máxima produtividade. Construído com tecnologia open source e apoiado por uma equipe dedicada de desenvolvedores junto a uma comunidade global de voluntários, o Thunderbird nunca trata seus dados privados como produto. Suportado apenas por contribuições financeiras dos nossos usuários, para que você nunca mais veja anúncios misturados aos seus e-mails.
-O que você pode fazer
+O que você pode fazer
-Diga adeus a múltiplos apps e webmail. Use um único aplicativo, com a Caixa de Entrada Unificada opcional, para agilizar seu dia.
+
Abandone múltiplos apps e webmails. Use um único app, com a opção de Caixa de Entrada Unificada, para otimizar seu dia.
Desfrute de um cliente de e-mail que respeita sua privacidade e nunca coleta ou vende seus dados pessoais. Conectamos você diretamente ao seu provedor de e-mail. Só isso!
Eleve sua privacidade usando criptografia de e-mail OpenPGP (PGP/MIME) com o app “OpenKeychain”, para criptografar e descriptografar suas mensagens.
Escolha sincronizar seu e-mail instantaneamente, em intervalos definidos ou sob demanda. A forma como você verifica seu e-mail é você quem decide!
Encontre suas mensagens importantes usando busca local e no servidor.
-Aproveite um cliente de e-mail que respeita sua privacidade e nunca coleta ou vende seus dados pessoais. Conectamos você diretamente ao seu provedor de e-mail. Só isso!
+Compatibilidade
-Leve sua privacidade a outro nível usando criptografia de e-mails OpenPGP (PGP/MIME) com o app OpenKeychain, para criptografar e descriptografar suas mensagens.
+
O Thunderbird funciona com os protocolos IMAP e POP3, suportando uma ampla variedade de provedores de e-mail, incluindo Gmail, Outlook, Yahoo Mail, iCloud e mais.
-Escolha sincronizar seus e-mails instantaneamente, em intervalos definidos ou sob demanda. Você decide como quer checar seu e-mail!
+Por que usar o Thunderbird
-Encontre suas mensagens importantes usando pesquisa local ou no servidor.
+
Nome de confiança em e-mails por mais de 20 anos – agora no Android.
O Thunderbird é totalmente financiado por contribuições voluntárias dos nossos usuários. Não coletamos seus dados pessoais. Você nunca é o produto.
Desenvolvido por uma equipe tão focada em eficiência quanto você. Queremos que você gaste o mínimo de tempo usando o app, obtendo o máximo em retorno.
Com colaboradores de todo o mundo, o Thunderbird para Android foi traduzido para mais de 20 idiomas.
Suportado pela MZLA Technologies Corporation, uma subsidiária integral da Mozilla Foundation.
-Compatibilidade
+Open Source e Comunidade
-O Thunderbird funciona com os protocolos IMAP e POP3, suportando uma ampla gama de provedores de e-mail, incluindo Gmail, Outlook, Yahoo Mail, iCloud e outros.
-
-Por que usar o Thunderbird
-
-Um nome de confiança em e-mails por mais de 20 anos – agora disponível para Android.
-
-O Thunderbird é totalmente financiado por contribuições voluntárias dos usuários. Não mineramos seus dados pessoais. Você nunca é o produto.
-
-Criado por uma equipe tão focada em eficiência quanto você. Queremos que você passe o mínimo de tempo usando o app, aproveitando o máximo dele.
-
-Com colaboradores de todo o mundo, o Thunderbird para Android foi traduzido para mais de 20 idiomas.
-
-Suportado pela MZLA Technologies Corporation, subsidiária integral da Mozilla Foundation.
-
-Código aberto e comunidade
-
-O Thunderbird é gratuito e de código aberto, o que significa que seu código está disponível para ver, modificar, usar e compartilhar livremente. Sua licença garante que ele será sempre gratuito. Você pode pensar no Thunderbird como um presente de milhares de colaboradores para você.
-
-Desenvolvemos de forma aberta, com atualizações regulares e transparentes em nosso blog e listas de discussão.
-
-Nosso suporte aos usuários é alimentado por nossa comunidade global. Encontre as respostas que precisa ou participe como colaborador – seja respondendo perguntas, traduzindo o app ou divulgando o Thunderbird para amigos e familiares.
+
O Thunderbird é gratuito e open source, o que significa que seu código está disponível para ser visto, modificado, usado e compartilhado livremente. Sua licença também garante que ele será gratuito para sempre. Você pode pensar no Thunderbird como um presente de milhares de colaboradores para você.
Desenvolvemos de forma aberta com atualizações regulares e transparentes em nosso blog e listas de discussão.
Nosso suporte ao usuário é impulsionado por nossa comunidade global. Encontre as respostas que precisa ou participe como colaborador – seja respondendo perguntas, traduzindo o app ou divulgando o Thunderbird para amigos e familiares.
diff --git a/app-metadata/net.thunderbird.android/eo/short_description.txt b/app-metadata/net.thunderbird.android/eo/short_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..61c36826a2a18ba2dbf6e6d09cbee362c3aff819
--- /dev/null
+++ b/app-metadata/net.thunderbird.android/eo/short_description.txt
@@ -0,0 +1 @@
+Thunderbird estas plene malfermitkoda, privateco-gardanta retpoŝtilo.
diff --git a/app-metadata/net.thunderbird.android/eo/title.txt b/app-metadata/net.thunderbird.android/eo/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a5d89e240c9c0610e3666c2511d3ae8a2965aaa2
--- /dev/null
+++ b/app-metadata/net.thunderbird.android/eo/title.txt
@@ -0,0 +1 @@
+Thunderbird: Libera retpoŝtilo
diff --git a/app-metadata/net.thunderbird.android/es-ES/title.txt b/app-metadata/net.thunderbird.android/es-ES/title.txt
index 4ac6ef767441241a4833066ea07649bd09b31ef2..2092e4080c996708f9f2ec718f55c6c5acc3064a 100644
--- a/app-metadata/net.thunderbird.android/es-ES/title.txt
+++ b/app-metadata/net.thunderbird.android/es-ES/title.txt
@@ -1 +1 @@
-Thunderbird para Android
+Thunderbird; libera tu correo
diff --git a/app-metadata/net.thunderbird.android/hi-IN/short_description.txt b/app-metadata/net.thunderbird.android/hi-IN/short_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5f81c3818d6ce387b7b5c4f52e7d4539826d7449
--- /dev/null
+++ b/app-metadata/net.thunderbird.android/hi-IN/short_description.txt
@@ -0,0 +1 @@
+थंडरबर्ड एक 100% खुला स्रोत, गोपनीयता केंद्रित ईमेल ऐप है।
diff --git a/app-metadata/net.thunderbird.android/hi-IN/title.txt b/app-metadata/net.thunderbird.android/hi-IN/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3efeabc574566b5239aa1d3a1b8b36bca872d747
--- /dev/null
+++ b/app-metadata/net.thunderbird.android/hi-IN/title.txt
@@ -0,0 +1 @@
+थंडरबर्ड - इन्बॉक्स मुक्त करें
diff --git a/app-metadata/net.thunderbird.android/mnw/title.txt b/app-metadata/net.thunderbird.android/mnw/title.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5fa191052dc86947d6370df806d2e059d09c9553
--- /dev/null
+++ b/app-metadata/net.thunderbird.android/mnw/title.txt
@@ -0,0 +1 @@
+ဂစေံလလဳ: သၠးကဠာလိက်မၞး
diff --git a/app-metadata/net.thunderbird.android/pt-BR/short_description.txt b/app-metadata/net.thunderbird.android/pt-BR/short_description.txt
index 3c71ae90d58f246b4bcc26a78114788b84a7b460..927c151c825ebb4f3b8265e4227f347f659c8e48 100644
--- a/app-metadata/net.thunderbird.android/pt-BR/short_description.txt
+++ b/app-metadata/net.thunderbird.android/pt-BR/short_description.txt
@@ -1 +1 @@
-Thunderbird é um aplicativo de email, totalmente de código aberto e com foco na privacidade.
+Thunderbird é um app de e-mail 100% open source e focado em privacidade.
diff --git a/app-metadata/net.thunderbird.android/th/short_description.txt b/app-metadata/net.thunderbird.android/th/short_description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9e2a87e42dbdfdb4b81f7e7f73faa2380baa41f9
--- /dev/null
+++ b/app-metadata/net.thunderbird.android/th/short_description.txt
@@ -0,0 +1 @@
+Thunderbird is a 100% open source, privacy focused email app.
diff --git a/app-thunderbird/badging/fossBeta-badging.txt b/app-thunderbird/badging/fossBeta-badging.txt
index ed8e7c15b50b7348cf116c667e6266d1aaa3ac3b..3bd2f8bf53a9552426194479cac626697090433f 100644
--- a/app-thunderbird/badging/fossBeta-badging.txt
+++ b/app-thunderbird/badging/fossBeta-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='net.thunderbird.android.beta' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='net.thunderbird.android.beta' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-thunderbird/badging/fossDaily-badging.txt b/app-thunderbird/badging/fossDaily-badging.txt
index 64163be6089062e0ecc24ff6eb65390747f8caac..e55ca6989f45358b96e5c92939476f0734e87315 100644
--- a/app-thunderbird/badging/fossDaily-badging.txt
+++ b/app-thunderbird/badging/fossDaily-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='net.thunderbird.android.daily' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='net.thunderbird.android.daily' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-thunderbird/badging/fossRelease-badging.txt b/app-thunderbird/badging/fossRelease-badging.txt
index fef671d676432da16478b6c06c106227352b5d32..b538d2886b6de8a718cc09ed605fbbb0ada23e78 100644
--- a/app-thunderbird/badging/fossRelease-badging.txt
+++ b/app-thunderbird/badging/fossRelease-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='net.thunderbird.android' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='net.thunderbird.android' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-thunderbird/badging/fullBeta-badging.txt b/app-thunderbird/badging/fullBeta-badging.txt
index 17ff54616b411bcdbeeef4225b7820aad5628065..1e4a87bcac8f9b4f57063b361f5da9a19ab13eec 100644
--- a/app-thunderbird/badging/fullBeta-badging.txt
+++ b/app-thunderbird/badging/fullBeta-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='net.thunderbird.android.beta' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='net.thunderbird.android.beta' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-thunderbird/badging/fullDaily-badging.txt b/app-thunderbird/badging/fullDaily-badging.txt
index d8582ea705e603c80abbf476ab5b6deb877a1cf4..ed9d025abd262cfaf247ce3a7552c693831479ab 100644
--- a/app-thunderbird/badging/fullDaily-badging.txt
+++ b/app-thunderbird/badging/fullDaily-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='net.thunderbird.android.daily' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='net.thunderbird.android.daily' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-thunderbird/badging/fullRelease-badging.txt b/app-thunderbird/badging/fullRelease-badging.txt
index 1fee8f8c3eddb962844ccbb104b7d9933dfcdc77..17ed8e73680ed3cfcf3480d47783e06d426fce98 100644
--- a/app-thunderbird/badging/fullRelease-badging.txt
+++ b/app-thunderbird/badging/fullRelease-badging.txt
@@ -74,7 +74,7 @@ native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
other-activities
other-receivers
other-services
-package: name='net.thunderbird.android' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+package: name='net.thunderbird.android' platformBuildVersionName='16' platformBuildVersionCode='36' compileSdkVersion='36' compileSdkVersionCodename='16'
property: name='android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE' value='This service is used to maintain a continuous connection to an IMAP server to be able to provide instant notifications to the user when a new email arrives. Firebase Cloud Messaging is not suitable for this task, neither are mechanisms like AndroidX WorkManager. Other foreground service types aren't a good fit for this use case.'
provides-component:'app-widget'
supports-any-density: 'true'
diff --git a/app-thunderbird/build.gradle.kts b/app-thunderbird/build.gradle.kts
index dcfb01958727f2555373995ed2a3c51dcbd0a577..28c30495a06157585a12d76a6213b87ec9d54e2e 100644
--- a/app-thunderbird/build.gradle.kts
+++ b/app-thunderbird/build.gradle.kts
@@ -18,7 +18,7 @@ android {
testApplicationId = "net.thunderbird.android.tests"
versionCode = 34
- versionName = "14.0"
+ versionName = "15.0"
buildConfigField("String", "CLIENT_INFO_APP_NAME", "\"Thunderbird for Android\"")
}
@@ -121,7 +121,7 @@ android {
signingConfig = signingConfigs.getByType(SigningType.TB_BETA)
applicationIdSuffix = ".beta"
- versionNameSuffix = "b3"
+ versionNameSuffix = "b1"
isMinifyEnabled = true
isShrinkResources = true
@@ -172,7 +172,6 @@ android {
}
}
- @Suppress("UnstableApiUsage")
bundle {
language {
// Don't split by language. Otherwise our in-app language switcher won't work.
@@ -253,7 +252,6 @@ dependencies {
implementation(projects.feature.migration.launcher.thunderbird)
// TODO remove once OAuth ids have been moved from TBD to TBA
- "betaImplementation"(libs.appauth)
releaseImplementation(libs.appauth)
// Required for DependencyInjectionTest
diff --git a/app-thunderbird/dependencies/fossBetaRuntimeClasspath.txt b/app-thunderbird/dependencies/fossBetaRuntimeClasspath.txt
index 6f18d9b53d85c91d2492f0eaf5270eaa4b3c0e5e..c4067960d1fec0acd939225cbbb01417b30213d2 100644
--- a/app-thunderbird/dependencies/fossBetaRuntimeClasspath.txt
+++ b/app-thunderbird/dependencies/fossBetaRuntimeClasspath.txt
@@ -42,12 +42,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -68,11 +68,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -105,28 +105,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -145,11 +145,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -177,15 +177,17 @@ co.touchlab:stately-concurrent-collections:2.1.0
co.touchlab:stately-strict-jvm:2.1.0
co.touchlab:stately-strict:2.1.0
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.flexbox:flexbox:3.0.0
com.google.android.material:material:1.12.0
com.google.auto.value:auto-value-annotations:1.6.3
@@ -200,10 +202,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -213,16 +215,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -243,14 +245,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -262,7 +264,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -270,12 +272,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-thunderbird/dependencies/fossDailyRuntimeClasspath.txt b/app-thunderbird/dependencies/fossDailyRuntimeClasspath.txt
index 6f18d9b53d85c91d2492f0eaf5270eaa4b3c0e5e..c4067960d1fec0acd939225cbbb01417b30213d2 100644
--- a/app-thunderbird/dependencies/fossDailyRuntimeClasspath.txt
+++ b/app-thunderbird/dependencies/fossDailyRuntimeClasspath.txt
@@ -42,12 +42,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -68,11 +68,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -105,28 +105,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -145,11 +145,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -177,15 +177,17 @@ co.touchlab:stately-concurrent-collections:2.1.0
co.touchlab:stately-strict-jvm:2.1.0
co.touchlab:stately-strict:2.1.0
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.flexbox:flexbox:3.0.0
com.google.android.material:material:1.12.0
com.google.auto.value:auto-value-annotations:1.6.3
@@ -200,10 +202,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -213,16 +215,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -243,14 +245,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -262,7 +264,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -270,12 +272,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-thunderbird/dependencies/fossReleaseRuntimeClasspath.txt b/app-thunderbird/dependencies/fossReleaseRuntimeClasspath.txt
index 6f18d9b53d85c91d2492f0eaf5270eaa4b3c0e5e..c4067960d1fec0acd939225cbbb01417b30213d2 100644
--- a/app-thunderbird/dependencies/fossReleaseRuntimeClasspath.txt
+++ b/app-thunderbird/dependencies/fossReleaseRuntimeClasspath.txt
@@ -42,12 +42,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -68,11 +68,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -105,28 +105,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -145,11 +145,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -177,15 +177,17 @@ co.touchlab:stately-concurrent-collections:2.1.0
co.touchlab:stately-strict-jvm:2.1.0
co.touchlab:stately-strict:2.1.0
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.flexbox:flexbox:3.0.0
com.google.android.material:material:1.12.0
com.google.auto.value:auto-value-annotations:1.6.3
@@ -200,10 +202,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -213,16 +215,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -243,14 +245,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -262,7 +264,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -270,12 +272,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-thunderbird/dependencies/fullBetaRuntimeClasspath.txt b/app-thunderbird/dependencies/fullBetaRuntimeClasspath.txt
index d4540eedfc8fdcc29dbb8c2174278ce695d51d52..2a6a5724b904be1487cb4bcd76f86c5c1d5de4e3 100644
--- a/app-thunderbird/dependencies/fullBetaRuntimeClasspath.txt
+++ b/app-thunderbird/dependencies/fullBetaRuntimeClasspath.txt
@@ -42,12 +42,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -68,11 +68,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -105,28 +105,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -145,11 +145,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -179,15 +179,17 @@ co.touchlab:stately-strict:2.1.0
com.android.billingclient:billing-ktx:7.1.1
com.android.billingclient:billing:7.1.1
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.datatransport:transport-api:3.0.0
com.google.android.datatransport:transport-backend-cct:3.1.8
com.google.android.datatransport:transport-runtime:3.1.8
@@ -213,10 +215,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -226,16 +228,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -257,14 +259,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -276,7 +278,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -284,12 +286,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-thunderbird/dependencies/fullDailyRuntimeClasspath.txt b/app-thunderbird/dependencies/fullDailyRuntimeClasspath.txt
index d4540eedfc8fdcc29dbb8c2174278ce695d51d52..2a6a5724b904be1487cb4bcd76f86c5c1d5de4e3 100644
--- a/app-thunderbird/dependencies/fullDailyRuntimeClasspath.txt
+++ b/app-thunderbird/dependencies/fullDailyRuntimeClasspath.txt
@@ -42,12 +42,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -68,11 +68,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -105,28 +105,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -145,11 +145,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -179,15 +179,17 @@ co.touchlab:stately-strict:2.1.0
com.android.billingclient:billing-ktx:7.1.1
com.android.billingclient:billing:7.1.1
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.datatransport:transport-api:3.0.0
com.google.android.datatransport:transport-backend-cct:3.1.8
com.google.android.datatransport:transport-runtime:3.1.8
@@ -213,10 +215,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -226,16 +228,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -257,14 +259,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -276,7 +278,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -284,12 +286,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-thunderbird/dependencies/fullReleaseRuntimeClasspath.txt b/app-thunderbird/dependencies/fullReleaseRuntimeClasspath.txt
index d4540eedfc8fdcc29dbb8c2174278ce695d51d52..2a6a5724b904be1487cb4bcd76f86c5c1d5de4e3 100644
--- a/app-thunderbird/dependencies/fullReleaseRuntimeClasspath.txt
+++ b/app-thunderbird/dependencies/fullReleaseRuntimeClasspath.txt
@@ -42,12 +42,12 @@ androidx.compose.material:material-icons-extended-android:1.7.8
androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.8.3
androidx.compose.material:material-ripple:1.8.3
-androidx.compose.runtime:runtime-android:1.9.0
-androidx.compose.runtime:runtime-annotation-android:1.9.0
-androidx.compose.runtime:runtime-annotation:1.9.0
-androidx.compose.runtime:runtime-saveable-android:1.9.0
-androidx.compose.runtime:runtime-saveable:1.9.0
-androidx.compose.runtime:runtime:1.9.0
+androidx.compose.runtime:runtime-android:1.9.4
+androidx.compose.runtime:runtime-annotation-android:1.9.4
+androidx.compose.runtime:runtime-annotation:1.9.4
+androidx.compose.runtime:runtime-saveable-android:1.9.4
+androidx.compose.runtime:runtime-saveable:1.9.4
+androidx.compose.runtime:runtime:1.9.4
androidx.compose.ui:ui-android:1.9.0
androidx.compose.ui:ui-geometry-android:1.9.0
androidx.compose.ui:ui-geometry:1.9.0
@@ -68,11 +68,11 @@ androidx.concurrent:concurrent-futures:1.1.0
androidx.constraintlayout:constraintlayout-core:1.1.1
androidx.constraintlayout:constraintlayout:2.2.1
androidx.coordinatorlayout:coordinatorlayout:1.3.0
-androidx.core:core-ktx:1.16.0
+androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0
androidx.core:core-splashscreen:1.0.1
androidx.core:core-viewtree:1.0.0
-androidx.core:core:1.16.0
+androidx.core:core:1.17.0
androidx.cursoradapter:cursoradapter:1.0.0
androidx.customview:customview-poolingcontainer:1.0.0
androidx.customview:customview:1.1.0
@@ -105,28 +105,28 @@ androidx.glance:glance:1.1.1
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
-androidx.lifecycle:lifecycle-common-java8:2.9.3
-androidx.lifecycle:lifecycle-common-jvm:2.9.3
-androidx.lifecycle:lifecycle-common:2.9.3
-androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata-core:2.9.3
-androidx.lifecycle:lifecycle-livedata-ktx:2.9.3
-androidx.lifecycle:lifecycle-livedata:2.9.3
-androidx.lifecycle:lifecycle-process:2.9.3
-androidx.lifecycle:lifecycle-runtime-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-compose:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.3
-androidx.lifecycle:lifecycle-runtime-ktx:2.9.3
-androidx.lifecycle:lifecycle-runtime:2.9.3
-androidx.lifecycle:lifecycle-service:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.3
-androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3
-androidx.lifecycle:lifecycle-viewmodel:2.9.3
+androidx.lifecycle:lifecycle-common-java8:2.9.4
+androidx.lifecycle:lifecycle-common-jvm:2.9.4
+androidx.lifecycle:lifecycle-common:2.9.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata-core:2.9.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.9.4
+androidx.lifecycle:lifecycle-livedata:2.9.4
+androidx.lifecycle:lifecycle-process:2.9.4
+androidx.lifecycle:lifecycle-runtime-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-compose:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.9.4
+androidx.lifecycle:lifecycle-runtime:2.9.4
+androidx.lifecycle:lifecycle-service:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
+androidx.lifecycle:lifecycle-viewmodel:2.9.4
androidx.loader:loader:1.0.0
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
androidx.navigation:navigation-common-android:2.9.3
@@ -145,11 +145,11 @@ androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
-androidx.savedstate:savedstate-android:1.3.1
-androidx.savedstate:savedstate-compose-android:1.3.1
-androidx.savedstate:savedstate-compose:1.3.1
-androidx.savedstate:savedstate-ktx:1.3.1
-androidx.savedstate:savedstate:1.3.1
+androidx.savedstate:savedstate-android:1.3.3
+androidx.savedstate:savedstate-compose-android:1.3.3
+androidx.savedstate:savedstate-compose:1.3.3
+androidx.savedstate:savedstate-ktx:1.3.3
+androidx.savedstate:savedstate:1.3.3
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
@@ -179,15 +179,17 @@ co.touchlab:stately-strict:2.1.0
com.android.billingclient:billing-ktx:7.1.1
com.android.billingclient:billing:7.1.1
com.beetstra.jutf7:jutf7:1.0.0
+com.eygraber:uri-kmp-android:0.0.21
+com.eygraber:uri-kmp:0.0.21
com.github.ByteHamster:SearchPreference:2.7.3
com.github.bumptech.glide:annotations:4.16.0
com.github.bumptech.glide:disklrucache:4.16.0
com.github.bumptech.glide:gifdecoder:4.16.0
com.github.bumptech.glide:glide:4.16.0
-com.github.skydoves:landscapist-android:2.5.1
-com.github.skydoves:landscapist-coil3-android:2.5.1
-com.github.skydoves:landscapist-coil3:2.5.1
-com.github.skydoves:landscapist:2.5.1
+com.github.skydoves:landscapist-android:2.6.1
+com.github.skydoves:landscapist-coil3-android:2.6.1
+com.github.skydoves:landscapist-coil3:2.6.1
+com.github.skydoves:landscapist:2.6.1
com.google.android.datatransport:transport-api:3.0.0
com.google.android.datatransport:transport-backend-cct:3.1.8
com.google.android.datatransport:transport-runtime:3.1.8
@@ -213,10 +215,10 @@ com.mikepenz:fastadapter-extensions-swipe:5.7.0
com.mikepenz:fastadapter-extensions-utils:5.7.0
com.mikepenz:fastadapter:5.7.0
com.squareup.moshi:moshi:1.15.2
-com.squareup.okhttp3:okhttp-android:5.1.0
-com.squareup.okhttp3:okhttp:5.1.0
-com.squareup.okio:okio-jvm:3.16.0
-com.squareup.okio:okio:3.16.0
+com.squareup.okhttp3:okhttp-android:5.2.1
+com.squareup.okhttp3:okhttp:5.2.1
+com.squareup.okio:okio-jvm:3.16.2
+com.squareup.okio:okio:3.16.2
com.takisoft.colorpicker:colorpicker:1.0.0
com.takisoft.datetimepicker:datetimepicker:1.0.2
com.takisoft.preferencex:preferencex-colorpicker:1.1.0
@@ -226,16 +228,16 @@ commons-io:commons-io:2.20.0
de.cketti.library.changelog:ckchangelog-core:2.0.0-beta02
de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0
de.hdodenhof:circleimageview:3.1.0
-io.coil-kt.coil3:coil-android:3.2.0
-io.coil-kt.coil3:coil-core-android:3.2.0
-io.coil-kt.coil3:coil-core:3.2.0
-io.coil-kt.coil3:coil-gif:3.2.0
-io.coil-kt.coil3:coil-network-core-android:3.2.0
-io.coil-kt.coil3:coil-network-core:3.2.0
-io.coil-kt.coil3:coil-network-okhttp-jvm:3.2.0
-io.coil-kt.coil3:coil-network-okhttp:3.2.0
-io.coil-kt.coil3:coil-video:3.2.0
-io.coil-kt.coil3:coil:3.2.0
+io.coil-kt.coil3:coil-android:3.3.0
+io.coil-kt.coil3:coil-core-android:3.3.0
+io.coil-kt.coil3:coil-core:3.3.0
+io.coil-kt.coil3:coil-gif:3.3.0
+io.coil-kt.coil3:coil-network-core-android:3.3.0
+io.coil-kt.coil3:coil-network-core:3.3.0
+io.coil-kt.coil3:coil-network-okhttp-jvm:3.3.0
+io.coil-kt.coil3:coil-network-okhttp:3.3.0
+io.coil-kt.coil3:coil-video:3.3.0
+io.coil-kt.coil3:coil:3.3.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-bom:4.1.1
@@ -257,14 +259,14 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.3.6
org.apache.httpcomponents.core5:httpcore5:5.3.6
org.apache.james:apache-mime4j-core:0.8.13
org.apache.james:apache-mime4j-dom:0.8.13
-org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4
-org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.4
+org.jetbrains.androidx.lifecycle:lifecycle-common:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.6
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.6
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.4
-org.jetbrains.androidx.savedstate:savedstate:1.3.4
+org.jetbrains.androidx.savedstate:savedstate:1.3.6
org.jetbrains.compose.animation:animation-core:1.9.0
org.jetbrains.compose.animation:animation:1.9.0
org.jetbrains.compose.annotation-internal:annotation:1.9.0
@@ -276,7 +278,7 @@ org.jetbrains.compose.components:components-ui-tooling-preview:1.9.0
org.jetbrains.compose.foundation:foundation-layout:1.9.0
org.jetbrains.compose.foundation:foundation:1.9.0
org.jetbrains.compose.runtime:runtime-saveable:1.9.0
-org.jetbrains.compose.runtime:runtime:1.9.0
+org.jetbrains.compose.runtime:runtime:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.9.0
org.jetbrains.compose.ui:ui-graphics:1.9.0
org.jetbrains.compose.ui:ui-text:1.9.0
@@ -284,12 +286,12 @@ org.jetbrains.compose.ui:ui-tooling-preview:1.9.0
org.jetbrains.compose.ui:ui-unit:1.9.0
org.jetbrains.compose.ui:ui-util:1.9.0
org.jetbrains.compose.ui:ui:1.9.0
-org.jetbrains.kotlin:kotlin-bom:2.2.20
-org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-common:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20
-org.jetbrains.kotlin:kotlin-stdlib:2.2.20
+org.jetbrains.kotlin:kotlin-bom:2.2.21
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21
+org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
diff --git a/app-thunderbird/src/beta/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt b/app-thunderbird/src/beta/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
index 6fce270d82e4a66119a8dbb57f086fe16a396572..a1852ca5a9d8c3a2dd15e5e3566780c395429cdf 100644
--- a/app-thunderbird/src/beta/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
+++ b/app-thunderbird/src/beta/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
@@ -1,10 +1,12 @@
package net.thunderbird.android.featureflag
import com.fsck.k9.ui.messagelist.MessageListFeatureFlags
+import com.fsck.k9.ui.messageview.MessageViewFeatureFlags
import net.thunderbird.core.featureflag.FeatureFlag
import net.thunderbird.core.featureflag.FeatureFlagFactory
import net.thunderbird.core.featureflag.FeatureFlagKey
import net.thunderbird.core.featureflag.toFeatureFlagKey
+import net.thunderbird.feature.account.settings.AccountSettingsFeatureFlags
/**
* Feature flags for Thunderbird Beta
@@ -18,9 +20,11 @@ class TbFeatureFlagFactory : FeatureFlagFactory {
FeatureFlag("email_notification_default".toFeatureFlagKey(), enabled = true),
FeatureFlag("enable_dropdown_drawer".toFeatureFlagKey(), enabled = true),
FeatureFlag("enable_dropdown_drawer_ui".toFeatureFlagKey(), enabled = true),
- FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = false),
+ FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = true),
FeatureFlag(FeatureFlagKey.UseNotificationSenderForSystemNotifications, enabled = false),
FeatureFlag(MessageListFeatureFlags.UseComposeForMessageListItems, enabled = false),
+ FeatureFlag(MessageViewFeatureFlags.ActionExportEml, enabled = false),
+ FeatureFlag(AccountSettingsFeatureFlags.EnableAvatarCustomization, enabled = false),
)
}
}
diff --git a/app-thunderbird/src/daily/AndroidManifest.xml b/app-thunderbird/src/daily/AndroidManifest.xml
index 689e9c615a44160357f6b334b6d42064f2fe809d..17d3572fff3d03070a4ea6aac501dd20d9d5f6b6 100644
--- a/app-thunderbird/src/daily/AndroidManifest.xml
+++ b/app-thunderbird/src/daily/AndroidManifest.xml
@@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
>
-
+
diff --git a/app-thunderbird/src/daily/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt b/app-thunderbird/src/daily/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
index 51cc37abb64acadb71e7889f9f32d9e72771a374..51a6f461fccb59a7164badb71802ecab3cf398c4 100644
--- a/app-thunderbird/src/daily/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
+++ b/app-thunderbird/src/daily/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
@@ -1,10 +1,12 @@
package net.thunderbird.android.featureflag
import com.fsck.k9.ui.messagelist.MessageListFeatureFlags
+import com.fsck.k9.ui.messageview.MessageViewFeatureFlags
import net.thunderbird.core.featureflag.FeatureFlag
import net.thunderbird.core.featureflag.FeatureFlagFactory
import net.thunderbird.core.featureflag.FeatureFlagKey
import net.thunderbird.core.featureflag.toFeatureFlagKey
+import net.thunderbird.feature.account.settings.AccountSettingsFeatureFlags
/**
* Feature flags for Thunderbird Daily
@@ -21,6 +23,8 @@ class TbFeatureFlagFactory : FeatureFlagFactory {
FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = true),
FeatureFlag(FeatureFlagKey.UseNotificationSenderForSystemNotifications, enabled = false),
FeatureFlag(MessageListFeatureFlags.UseComposeForMessageListItems, enabled = false),
+ FeatureFlag(MessageViewFeatureFlags.ActionExportEml, enabled = true),
+ FeatureFlag(AccountSettingsFeatureFlags.EnableAvatarCustomization, enabled = false),
)
}
}
diff --git a/app-thunderbird/src/debug/AndroidManifest.xml b/app-thunderbird/src/debug/AndroidManifest.xml
index 689e9c615a44160357f6b334b6d42064f2fe809d..17d3572fff3d03070a4ea6aac501dd20d9d5f6b6 100644
--- a/app-thunderbird/src/debug/AndroidManifest.xml
+++ b/app-thunderbird/src/debug/AndroidManifest.xml
@@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
>
-
+
diff --git a/app-thunderbird/src/debug/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt b/app-thunderbird/src/debug/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
index 174a8ee9751803942416e9379be8bf427749ef59..88a2b9890051bc3afed84da19f50962f9226d397 100644
--- a/app-thunderbird/src/debug/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
+++ b/app-thunderbird/src/debug/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
@@ -1,10 +1,12 @@
package net.thunderbird.android.featureflag
import com.fsck.k9.ui.messagelist.MessageListFeatureFlags
+import com.fsck.k9.ui.messageview.MessageViewFeatureFlags
import net.thunderbird.core.featureflag.FeatureFlag
import net.thunderbird.core.featureflag.FeatureFlagFactory
import net.thunderbird.core.featureflag.FeatureFlagKey
import net.thunderbird.core.featureflag.toFeatureFlagKey
+import net.thunderbird.feature.account.settings.AccountSettingsFeatureFlags
/**
* Feature flags for Thunderbird Debug
@@ -21,6 +23,8 @@ class TbFeatureFlagFactory : FeatureFlagFactory {
FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = true),
FeatureFlag(FeatureFlagKey.UseNotificationSenderForSystemNotifications, enabled = true),
FeatureFlag(MessageListFeatureFlags.UseComposeForMessageListItems, enabled = false),
+ FeatureFlag(MessageViewFeatureFlags.ActionExportEml, enabled = true),
+ FeatureFlag(AccountSettingsFeatureFlags.EnableAvatarCustomization, enabled = false),
)
}
}
diff --git a/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/ProviderModule.kt b/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/ProviderModule.kt
index cf7d989eefd8ed83bb97d6d5e63bf3cf0ec48828..cd46613fe834430ca2936f1b192e9a2598c401fb 100644
--- a/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/ProviderModule.kt
+++ b/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/ProviderModule.kt
@@ -1,5 +1,6 @@
package net.thunderbird.android.provider
+import app.k9mail.core.android.common.provider.NotificationIconResourceProvider
import com.fsck.k9.preferences.FilePrefixProvider
import net.thunderbird.core.common.provider.AppNameProvider
import net.thunderbird.core.common.provider.BrandNameProvider
@@ -17,4 +18,8 @@ internal val providerModule = module {
single { TbThemeProvider() }
single { TbFeatureThemeProvider() }
+
+ single {
+ TbAppIconNotificationProvider()
+ }
}
diff --git a/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/TbAppIconNotificationProvider.kt b/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/TbAppIconNotificationProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6cbaba6179eac49af6a25dc6b3936c32afc26a6a
--- /dev/null
+++ b/app-thunderbird/src/main/kotlin/net/thunderbird/android/provider/TbAppIconNotificationProvider.kt
@@ -0,0 +1,8 @@
+package net.thunderbird.android.provider
+
+import app.k9mail.core.android.common.provider.NotificationIconResourceProvider
+
+class TbAppIconNotificationProvider : NotificationIconResourceProvider {
+ override val pushNotificationIcon: Int
+ get() = app.k9mail.core.ui.legacy.theme2.thunderbird.R.drawable.ic_logo_thunderbird_white
+}
diff --git a/app-thunderbird/src/main/res/values-ht/strings.xml b/app-thunderbird/src/main/res/values-ht/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-thunderbird/src/main/res/values-ht/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-thunderbird/src/main/res/values-kn/strings.xml b/app-thunderbird/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-thunderbird/src/main/res/values-kn/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-thunderbird/src/main/res/values-mnw/strings.xml b/app-thunderbird/src/main/res/values-mnw/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/app-thunderbird/src/main/res/values-mnw/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app-thunderbird/src/release/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt b/app-thunderbird/src/release/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
index d5dce4a7658aa10a31e34ec4cc381d581aa3bc12..295060007a2efae581b7862f174cf750116069bd 100644
--- a/app-thunderbird/src/release/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
+++ b/app-thunderbird/src/release/kotlin/net/thunderbird/android/featureflag/TbFeatureFlagFactory.kt
@@ -1,10 +1,12 @@
package net.thunderbird.android.featureflag
import com.fsck.k9.ui.messagelist.MessageListFeatureFlags
+import com.fsck.k9.ui.messageview.MessageViewFeatureFlags
import net.thunderbird.core.featureflag.FeatureFlag
import net.thunderbird.core.featureflag.FeatureFlagFactory
import net.thunderbird.core.featureflag.FeatureFlagKey
import net.thunderbird.core.featureflag.toFeatureFlagKey
+import net.thunderbird.feature.account.settings.AccountSettingsFeatureFlags
/**
* Feature flags for Thunderbird (release)
@@ -21,6 +23,8 @@ class TbFeatureFlagFactory : FeatureFlagFactory {
FeatureFlag(FeatureFlagKey.DisplayInAppNotifications, enabled = false),
FeatureFlag(FeatureFlagKey.UseNotificationSenderForSystemNotifications, enabled = false),
FeatureFlag(MessageListFeatureFlags.UseComposeForMessageListItems, enabled = false),
+ FeatureFlag(MessageViewFeatureFlags.ActionExportEml, enabled = false),
+ FeatureFlag(AccountSettingsFeatureFlags.EnableAvatarCustomization, enabled = false),
)
}
}
diff --git a/app-thunderbird/src/test/kotlin/net/thunderbird/android/TbAppIconNotificationProviderTest.kt b/app-thunderbird/src/test/kotlin/net/thunderbird/android/TbAppIconNotificationProviderTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..86dd8c284d01a44b91e52e460315b85e51583506
--- /dev/null
+++ b/app-thunderbird/src/test/kotlin/net/thunderbird/android/TbAppIconNotificationProviderTest.kt
@@ -0,0 +1,17 @@
+package net.thunderbird.android
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import net.thunderbird.android.provider.TbAppIconNotificationProvider
+import org.junit.Test
+
+class TbAppIconNotificationProviderTest {
+ @Test
+ fun `provides correct Thunderbird notification icon`() {
+ val provider = TbAppIconNotificationProvider()
+ val icon = provider.pushNotificationIcon
+
+ assertThat(icon)
+ .isEqualTo(app.k9mail.core.ui.legacy.theme2.thunderbird.R.drawable.ic_logo_thunderbird_white)
+ }
+}
diff --git a/build-plugin/build.gradle.kts b/build-plugin/build.gradle.kts
index e3bd34c04ffa93082f7660cccb2056b63fa1080a..3aa0e61daffb1ef727983d090a7f93006625139d 100644
--- a/build-plugin/build.gradle.kts
+++ b/build-plugin/build.gradle.kts
@@ -30,6 +30,12 @@ dependencies {
implementation(platform(libs.kotlin.gradle.bom))
}
+kotlin {
+ compilerOptions {
+ allWarningsAsErrors = true
+ }
+}
+
fun plugin(provider: Provider) = with(provider.get()) {
"$pluginId:$pluginId.gradle.plugin:$version"
}
diff --git a/build-plugin/src/main/kotlin/AndroidExtension.kt b/build-plugin/src/main/kotlin/AndroidExtension.kt
index 81395391ec03f9ba01da4560b6b962bdae116abc..b0a1e6d731299797e7f39b741011f6971b029eb9 100644
--- a/build-plugin/src/main/kotlin/AndroidExtension.kt
+++ b/build-plugin/src/main/kotlin/AndroidExtension.kt
@@ -28,12 +28,6 @@ internal fun CommonExtension<*, *, *, *, *, *>.configureSharedConfig(project: Pr
checkReleaseBuilds = System.getenv("CI_CHECK_RELEASE_BUILDS")?.toBoolean() ?: true
}
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- }
- }
-
packaging {
resources {
excludes += listOf(
diff --git a/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt b/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt
index 6739d8c99fd375d2d902139830b38b76f1f93033..cf9f38ee9dc2d6a559a51ba27b376df8dfdbf9ad 100644
--- a/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt
+++ b/build-plugin/src/main/kotlin/ThunderbirdProjectConfig.kt
@@ -8,7 +8,7 @@ object ThunderbirdProjectConfig {
// Only needed for application
const val sdkTarget = 35
- const val sdkCompile = 35
+ const val sdkCompile = 36
}
object Compiler {
diff --git a/build-plugin/src/main/kotlin/thunderbird.app.android.gradle.kts b/build-plugin/src/main/kotlin/thunderbird.app.android.gradle.kts
index 5f1a2681635fae1b6839209daec73e6cb1a2c843..7f27a1f4332fe5d651a7c401146909a3e4df2efe 100644
--- a/build-plugin/src/main/kotlin/thunderbird.app.android.gradle.kts
+++ b/build-plugin/src/main/kotlin/thunderbird.app.android.gradle.kts
@@ -20,16 +20,18 @@ android {
isCoreLibraryDesugaringEnabled = true
}
- kotlinOptions {
- jvmTarget = ThunderbirdProjectConfig.Compiler.javaCompatibility.toString()
- }
-
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
}
+kotlin {
+ compilerOptions {
+ jvmTarget = ThunderbirdProjectConfig.Compiler.jvmTarget
+ }
+}
+
dependencies {
coreLibraryDesugaring(libs.android.desugar.nio)
diff --git a/build-plugin/src/main/kotlin/thunderbird.library.android.compose.gradle.kts b/build-plugin/src/main/kotlin/thunderbird.library.android.compose.gradle.kts
index c1e6087f2c1783abc2ffce9baebff51ddd31499a..efcf8538d796d17a71da609f09248e5bd4ba6307 100644
--- a/build-plugin/src/main/kotlin/thunderbird.library.android.compose.gradle.kts
+++ b/build-plugin/src/main/kotlin/thunderbird.library.android.compose.gradle.kts
@@ -1,3 +1,5 @@
+import com.android.build.api.variant.HostTestBuilder
+
plugins {
id("thunderbird.library.android")
id("org.jetbrains.kotlin.plugin.compose")
@@ -12,7 +14,7 @@ android {
androidComponents {
beforeVariants(selector().withBuildType("release")) { variantBuilder ->
- variantBuilder.enableUnitTest = false
+ variantBuilder.hostTests[HostTestBuilder.UNIT_TEST_TYPE]?.enable = false
variantBuilder.enableAndroidTest = false
}
}
diff --git a/build-plugin/src/main/kotlin/thunderbird.library.android.gradle.kts b/build-plugin/src/main/kotlin/thunderbird.library.android.gradle.kts
index 75a1077b660de9719b71ef20c435b48fec626448..7a16e7bf6f03afe9ba05103ec83d8ce936792f36 100644
--- a/build-plugin/src/main/kotlin/thunderbird.library.android.gradle.kts
+++ b/build-plugin/src/main/kotlin/thunderbird.library.android.gradle.kts
@@ -11,19 +11,12 @@ android {
buildFeatures {
buildConfig = false
}
-
- kotlinOptions {
- jvmTarget = ThunderbirdProjectConfig.Compiler.javaCompatibility.toString()
- }
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- }
- }
}
kotlin {
+ compilerOptions {
+ jvmTarget.set(ThunderbirdProjectConfig.Compiler.jvmTarget)
+ }
sourceSets.all {
compilerOptions {
freeCompilerArgs.add("-Xwhen-guards")
diff --git a/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/ResourceMoverCli.kt b/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/ResourceMoverCli.kt
index bc0dd1a08fef9f4514590e6f0fd9f15a315e0888..86271d74a342704edb282341c849b2cab3038873 100644
--- a/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/ResourceMoverCli.kt
+++ b/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/ResourceMoverCli.kt
@@ -12,15 +12,16 @@ class ResourceMoverCli(
name = "resource-mover",
) {
private val from: String by option(
- help = "Source module path",
+ help = "Source module path.",
).required()
private val to: String by option(
- help = "Target module path",
+ help = "Target module path. If the target module is a KMP module, ensure the " +
+ "commonMain/composeResources, otherwise the CLI won't be able to detect it automatically.",
).required()
private val keys: List by option(
- help = "Keys to move",
+ help = "Keys to move. When multiple separate by ','.",
).split(",").required()
override fun help(context: Context): String = "Move string resources from one file to another"
diff --git a/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/StringResourceMover.kt b/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/StringResourceMover.kt
index ddfb264488a730d2534f6095d749493fcadef3e5..1ab7859e9598614b48d414a2f1e7cee3d1560023 100644
--- a/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/StringResourceMover.kt
+++ b/cli/resource-mover-cli/src/main/kotlin/net/thunderbird/cli/resource/mover/StringResourceMover.kt
@@ -7,8 +7,16 @@ import kotlin.system.exitProcess
class StringResourceMover {
fun moveKeys(source: String, target: String, keys: List) {
- val sourcePath = File(source + RESOURCE_PATH)
- val targetPath = File(target + RESOURCE_PATH)
+ fun File.isComposeResource(): Boolean = File(this, COMPOSE_RESOURCE_PATH).exists()
+
+ val sourceDir = File(source)
+ val sourceBaseResourcePath = if (sourceDir.isComposeResource()) COMPOSE_RESOURCE_PATH else RESOURCE_PATH
+ val sourcePath = File(source + sourceBaseResourcePath)
+
+ val targetDir = File(target)
+ val isTargetComposeResources = targetDir.isComposeResource()
+ val targetBaseResourcePath = if (isTargetComposeResources) COMPOSE_RESOURCE_PATH else RESOURCE_PATH
+ val targetPath = File(target + targetBaseResourcePath)
if (!sourcePath.exists()) {
println("\nSource path does not exist: $sourcePath\n")
@@ -18,11 +26,11 @@ class StringResourceMover {
println("\nMoving keys $keys")
println(" from \"$sourcePath\" -> \"$targetPath\"\n")
for (key in keys) {
- moveKey(sourcePath, targetPath, key)
+ moveKey(sourcePath, targetPath, key, isTargetComposeResources)
}
}
- private fun moveKey(sourcePath: File, targetPath: File, key: String) {
+ private fun moveKey(sourcePath: File, targetPath: File, key: String, isTargetComposeResources: Boolean) {
println("\nMoving key: $key\n")
sourcePath.walk()
@@ -30,22 +38,30 @@ class StringResourceMover {
.forEach { sourceDir ->
val sourceFile = sourceDir.resolve(STRING_RESOURCE_FILE_NAME)
if (sourceFile.exists()) {
- moveKeyDeclaration(sourceFile, targetPath, key)
+ moveKeyDeclaration(sourceFile, targetPath, key, isTargetComposeResources)
}
}
}
- private fun moveKeyDeclaration(sourceFile: File, targetPath: File, key: String) {
+ private fun moveKeyDeclaration(sourceFile: File, targetPath: File, key: String, isTargetComposeResources: Boolean) {
if (containsKey(sourceFile, key)) {
println("\nFound key in file: ${sourceFile.path}\n")
- val targetFile = getOrCreateTargetFile(targetPath, sourceFile)
- val keyDeclaration = extractKeyDeclaration(sourceFile, key)
+ val targetFile = getOrCreateTargetFile(targetPath, sourceFile, isTargetComposeResources)
+ val originalKeyDeclaration = extractKeyDeclaration(sourceFile, key)
+ val keyDeclaration = originalKeyDeclaration.let { keyDeclaration ->
+ if (isTargetComposeResources && keyDeclaration.contains(")(.*?)()""".toRegex()
+ keyDeclaration.replace(regex, "$2")
+ } else {
+ keyDeclaration
+ }
+ }
println(" Key declaration: $keyDeclaration")
copyKeyToTarget(targetFile, keyDeclaration, key)
- deleteKeyFromSource(sourceFile, keyDeclaration)
+ deleteKeyFromSource(sourceFile, originalKeyDeclaration)
if (isSourceFileEmpty(sourceFile)) {
println(" Source file is empty: ${sourceFile.path} -> deleting it.")
@@ -134,7 +150,7 @@ class StringResourceMover {
return sourceContent.contains(STRING_CLOSING_TAG).not() && sourceContent.contains(PLURALS_CLOSING_TAG).not()
}
- private fun getOrCreateTargetFile(targetPath: File, sourceFile: File): File {
+ private fun getOrCreateTargetFile(targetPath: File, sourceFile: File, isTargetComposeResources: Boolean): File {
val targetFilePath = targetPath.resolve(sourceFile.parentFile.name)
val targetFile = File(targetFilePath, sourceFile.name)
val targetDirectory = targetFile.parentFile
@@ -145,20 +161,22 @@ class StringResourceMover {
}
if (!targetFile.exists()) {
- createTargetFile(targetFile)
+ createTargetFile(targetFile, isTargetComposeResources)
}
return targetFile
}
- private fun createTargetFile(targetFile: File) {
+ private fun createTargetFile(targetFile: File, isTargetComposeResources: Boolean) {
val isNewFileCreated: Boolean = targetFile.createNewFile()
if (!isNewFileCreated) {
printError("Target file could not be created: ${targetFile.path}")
exitProcess(-1)
}
- targetFile.writeText(TARGET_FILE_CONTENT)
+ targetFile.writeText(
+ if (isTargetComposeResources) TARGET_FILE_CONTENT_COMPOSE_RESOURCE else TARGET_FILE_CONTENT,
+ )
println("Target file ${targetFile.path} created")
}
@@ -168,6 +186,7 @@ class StringResourceMover {
private companion object {
const val RESOURCE_PATH = "/src/main/res/"
+ const val COMPOSE_RESOURCE_PATH = "/src/commonMain/composeResources/"
const val KEY_PLACEHOLDER = "{KEY}"
const val KEY_PATTERN = """name="$KEY_PLACEHOLDER""""
const val VALUES_PATH = "values"
@@ -182,5 +201,12 @@ class StringResourceMover {
""".trimIndent()
+
+ val TARGET_FILE_CONTENT_COMPOSE_RESOURCE = """
+
+
+
+
+ """.trimIndent()
}
}
diff --git a/config/lint/lint.xml b/config/lint/lint.xml
index b17c6187f6aba9e7b7130402ec27322960dda056..ae75933115da02d804772d621dbe353cf30443ba 100644
--- a/config/lint/lint.xml
+++ b/config/lint/lint.xml
@@ -6,7 +6,6 @@
-
diff --git a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountDefaultsProvider.kt b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountDefaultsProvider.kt
index b7dc95b1a197fb6695e1da5ad3e9df03f2dc949f..33e86b187e044b877e11832f41cee879ffa44209 100644
--- a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountDefaultsProvider.kt
+++ b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/AccountDefaultsProvider.kt
@@ -41,7 +41,7 @@ interface AccountDefaultsProvider {
const val DEFAULT_STRIP_SIGNATURE = true
- const val DEFAULT_SYNC_INTERVAL = 60
+ const val DEFAULT_SYNC_INTERVAL = 15
/**
* Specifies how many messages will be shown in a folder by default. This number is set
diff --git a/core/android/common/src/main/kotlin/app/k9mail/core/android/common/contact/ContactRepository.kt b/core/android/common/src/main/kotlin/app/k9mail/core/android/common/contact/ContactRepository.kt
index ef97d63a055bb3180fad2c4e554ec2daefa48896..a03105d31c108619ccec5d115776be46bb7d96de 100644
--- a/core/android/common/src/main/kotlin/app/k9mail/core/android/common/contact/ContactRepository.kt
+++ b/core/android/common/src/main/kotlin/app/k9mail/core/android/common/contact/ContactRepository.kt
@@ -1,7 +1,9 @@
package app.k9mail.core.android.common.contact
+import android.net.Uri
import net.thunderbird.core.common.cache.Cache
import net.thunderbird.core.common.mail.EmailAddress
+import net.thunderbird.core.common.mail.toEmailAddressOrNull
interface ContactRepository {
@@ -10,6 +12,8 @@ interface ContactRepository {
fun hasContactFor(emailAddress: EmailAddress): Boolean
fun hasAnyContactFor(emailAddresses: List): Boolean
+
+ fun getPhotoUri(emailAddress: String): Uri?
}
interface CachingRepository {
@@ -42,6 +46,12 @@ internal class CachingContactRepository(
override fun hasAnyContactFor(emailAddresses: List): Boolean =
emailAddresses.any { emailAddress -> hasContactFor(emailAddress) }
+ override fun getPhotoUri(emailAddress: String): Uri? {
+ return emailAddress.toEmailAddressOrNull()?.let { emailAddress ->
+ getContactFor(emailAddress)?.photoUri
+ }
+ }
+
override fun clearCache() {
cache.clear()
}
diff --git a/core/android/common/src/main/kotlin/app/k9mail/core/android/common/provider/NotificationIconResourceProvider.kt b/core/android/common/src/main/kotlin/app/k9mail/core/android/common/provider/NotificationIconResourceProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..014283ae9f0c2a32591afdecaad31744326428fa
--- /dev/null
+++ b/core/android/common/src/main/kotlin/app/k9mail/core/android/common/provider/NotificationIconResourceProvider.kt
@@ -0,0 +1,5 @@
+package app.k9mail.core.android.common.provider
+
+interface NotificationIconResourceProvider {
+ val pushNotificationIcon: Int
+}
diff --git a/core/android/common/src/test/kotlin/app/k9mail/core/android/common/CoreCommonAndroidModuleKtTest.kt b/core/android/common/src/test/kotlin/app/k9mail/core/android/common/CoreCommonAndroidModuleKtTest.kt
index 97a34d1222250e0307e6b750d373f691d2461063..00826960e84bfc39c4e0a1fd21e068f0c1d72eef 100644
--- a/core/android/common/src/test/kotlin/app/k9mail/core/android/common/CoreCommonAndroidModuleKtTest.kt
+++ b/core/android/common/src/test/kotlin/app/k9mail/core/android/common/CoreCommonAndroidModuleKtTest.kt
@@ -2,10 +2,12 @@ package app.k9mail.core.android.common
import android.content.Context
import org.junit.Test
+import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.test.verify.verify
internal class CoreCommonAndroidModuleKtTest {
+ @OptIn(KoinExperimentalAPI::class)
@Test
fun `should have a valid di module`() {
coreCommonAndroidModule.verify(
diff --git a/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/CachingContactRepositoryTest.kt b/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/CachingContactRepositoryTest.kt
index a4eed9a1e6f016d6cfd7d99ee38925e68882893b..3854450ef7c2800f7501dd1d7cabcfe1179a038a 100644
--- a/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/CachingContactRepositoryTest.kt
+++ b/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/CachingContactRepositoryTest.kt
@@ -1,5 +1,6 @@
package app.k9mail.core.android.common.contact
+import android.net.Uri
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isFalse
@@ -140,4 +141,53 @@ internal class CachingContactRepositoryTest {
assertThat(cache[CONTACT_EMAIL_ADDRESS]).isNull()
}
+
+ @Test
+ fun `getPhotoUri() returns null when email is invalid`() {
+ val result = testSubject.getPhotoUri("invalid-email")
+
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `getPhotoUri() returns null when no contact found for valid email`() {
+ dataSource.stub { on { getContactFor(CONTACT_EMAIL_ADDRESS) } doReturn null }
+
+ val result = testSubject.getPhotoUri(CONTACT_EMAIL_ADDRESS.address)
+
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun `getPhotoUri() returns contact photo uri when contact exists`() {
+ dataSource.stub { on { getContactFor(CONTACT_EMAIL_ADDRESS) } doReturn CONTACT }
+
+ val result = testSubject.getPhotoUri(CONTACT_EMAIL_ADDRESS.address)
+
+ assertThat(result).isEqualTo(CONTACT.photoUri)
+ }
+
+ @Test
+ fun `getPhotoUri() returns cached photo uri when contact already cached`() {
+ cache[CONTACT_EMAIL_ADDRESS] = CONTACT
+
+ val result = testSubject.getPhotoUri(CONTACT_EMAIL_ADDRESS.address)
+
+ assertThat(result).isEqualTo(CONTACT.photoUri)
+ }
+
+ @Test
+ fun `getPhotoUri() caches result after first fetch`() {
+ dataSource.stub {
+ on { getContactFor(CONTACT_EMAIL_ADDRESS) } doReturnConsecutively listOf(
+ CONTACT,
+ CONTACT.copy(photoUri = Uri.parse("content://other/photo")),
+ )
+ }
+
+ val result1 = testSubject.getPhotoUri(CONTACT_EMAIL_ADDRESS.address)
+ val result2 = testSubject.getPhotoUri(CONTACT_EMAIL_ADDRESS.address)
+
+ assertThat(result1).isEqualTo(result2)
+ }
}
diff --git a/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/ContactKoinModuleKtTest.kt b/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/ContactKoinModuleKtTest.kt
index 8b9442979bf587e6c48a2c50c726520293374152..f72ad5acf59aab91ee5177852de4a45b4021528c 100644
--- a/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/ContactKoinModuleKtTest.kt
+++ b/core/android/common/src/test/kotlin/app/k9mail/core/android/common/contact/ContactKoinModuleKtTest.kt
@@ -1,10 +1,12 @@
package app.k9mail.core.android.common.contact
import org.junit.Test
+import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.test.verify.verify
internal class ContactKoinModuleKtTest {
+ @OptIn(KoinExperimentalAPI::class)
@Test
fun `should have a valid di module`() {
contactModule.verify()
diff --git a/core/common/src/commonMain/kotlin/net/thunderbird/core/common/collections/PriorityQueue.kt b/core/common/src/commonMain/kotlin/net/thunderbird/core/common/collections/PriorityQueue.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3caf595dd73b49dd5635e7e5cd6e55c26808b82f
--- /dev/null
+++ b/core/common/src/commonMain/kotlin/net/thunderbird/core/common/collections/PriorityQueue.kt
@@ -0,0 +1,177 @@
+package net.thunderbird.core.common.collections
+
+/**
+ * An unbounded priority queue based on a binary heap.
+ *
+ * The elements of the priority queue are ordered according to the provided [Comparator].
+ * The head of this queue is the *least* element with respect to the specified ordering.
+ * If multiple elements are tied for least value, the head is one of those elements — ties
+ * are broken arbitrarily.
+ *
+ * The queue does not permit `null` elements.
+ *
+ * This class and its iterator implement all of the *optional* methods of the [Collection] and
+ * [Iterator] interfaces. The iterator returned by the `iterator()` method is *not* guaranteed
+ * to traverse the elements of the priority queue in any particular order.
+ * If you need ordered traversal, consider draining the queue into a list, for example:
+ *
+ * ```kotlin
+ * val list = mutableListOf()
+ * while (queue.isNotEmpty()) {
+ * list.add(queue.poll())
+ * }
+ * ```
+ *
+ * This implementation is not thread-safe.
+ *
+ * @param T The type of elements held in this collection.
+ * @param comparator The comparator that will be used to order this priority queue.
+ * @param elements An optional list of initial elements to be added to the queue.
+ */
+class PriorityQueue(
+ private val comparator: Comparator,
+ elements: List = emptyList(),
+) : AbstractQueue() {
+ private val heap = elements.toMutableList()
+
+ init {
+ if (heap.isNotEmpty()) {
+ heapify()
+ }
+ }
+
+ override val size: Int get() = heap.size
+ override fun isEmpty(): Boolean = heap.isEmpty()
+
+ override fun iterator(): MutableIterator = HeapIterator()
+
+ override fun offer(element: T): Boolean {
+ heap.add(element)
+ siftUp(heap.lastIndex)
+ return true
+ }
+
+ override fun poll(): T? {
+ if (heap.isEmpty()) {
+ return null
+ }
+ val result = heap.first()
+ val last = heap.removeAt(heap.lastIndex)
+ if (heap.isNotEmpty()) {
+ heap[0] = last
+ siftDown(0)
+ }
+ return result
+ }
+
+ override fun peek(): T? = heap.firstOrNull()
+
+ private fun siftUp(index: Int) {
+ var childIndex = index
+ fun parentIndex(childIndex: Int) = (childIndex - 1) / 2
+
+ while (childIndex > 0 && comparator.compare(heap[childIndex], heap[parentIndex(childIndex)]) < 0) {
+ swap(childIndex, parentIndex(childIndex))
+ childIndex = parentIndex(childIndex)
+ }
+ }
+
+ private fun siftDown(index: Int) {
+ var parentIndex = index
+ fun leftChildIndex(parentIndex: Int) = (parentIndex * 2) + 1
+ val half = heap.size / 2
+ while (parentIndex < half) {
+ var childIndex = leftChildIndex(parentIndex)
+ val rightChildIndex = childIndex + 1
+ if (rightChildIndex < heap.size &&
+ comparator.compare(heap[childIndex], heap[rightChildIndex]) > 0
+ ) {
+ childIndex = rightChildIndex
+ }
+
+ if (comparator.compare(heap[parentIndex], heap[childIndex]) <= 0) {
+ break
+ }
+
+ swap(parentIndex, childIndex)
+ parentIndex = childIndex
+ }
+ }
+
+ private fun heapify() {
+ for (index in ((heap.lastIndex - 1) / 2) downTo 0) {
+ siftDown(index)
+ }
+ }
+
+ private fun swap(first: Int, second: Int) {
+ val temp = heap[first]
+ heap[first] = heap[second]
+ heap[second] = temp
+ }
+
+ private fun removeAt(i: Int) {
+ val lastIndex = heap.lastIndex
+ if (i == lastIndex) {
+ heap.removeAt(lastIndex)
+ return
+ }
+ val moved = heap.removeAt(lastIndex)
+ heap[i] = moved
+ siftDown(i)
+ siftUp(i)
+ }
+
+ private inner class HeapIterator : MutableIterator {
+ private var index = 0
+ private var lastReturned = -1
+
+ override fun hasNext(): Boolean = index < heap.size
+
+ override fun next(): T {
+ if (!hasNext()) throw NoSuchElementException("Queue is empty")
+ lastReturned = index++
+ return heap[lastReturned]
+ }
+
+ override fun remove() {
+ require(lastReturned >= 0) { "next() has not been called" }
+ removeAt(lastReturned)
+ index = lastReturned--
+ }
+ }
+}
+
+/**
+ * Creates a [PriorityQueue] that orders its elements according to their natural-order,
+ * resulting in a min-priority queue (the smallest element is at the head).
+ *
+ * The elements must be [Comparable].
+ *
+ * @param T the type of elements in the priority queue.
+ * @param elements an optional [Iterable] of initial elements to be added to the queue.
+ * @return a [PriorityQueue] containing the specified elements, ordered by their natural ascending order.
+ */
+inline fun minPriorityQueueOf(
+ elements: Iterable = emptyList(),
+): PriorityQueue where T : Comparable = PriorityQueue(
+ comparator = { a, b -> a.compareTo(b) },
+ elements = elements.toList(),
+)
+
+/**
+ * Creates a [PriorityQueue] that orders its elements according to their reverse-order,
+ * resulting in a max-priority queue (the largest element is at the head).
+ *
+ * The elements must be [Comparable].
+ *
+ * @param T the type of elements in the priority queue.
+ * @param elements an optional [Iterable] of initial elements to be added to the queue.
+ * @return a [PriorityQueue] containing the specified elements, ordered by their natural descending order.
+ */
+inline fun maxPriorityQueueOf(
+ elements: Iterable = emptyList(),
+): PriorityQueue where T : Comparable = PriorityQueue(
+ comparator = { a, b -> b.compareTo(a) },
+ elements = elements.toList(),
+)
diff --git a/core/common/src/commonMain/kotlin/net/thunderbird/core/common/collections/Queue.kt b/core/common/src/commonMain/kotlin/net/thunderbird/core/common/collections/Queue.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6874d0f2a3fdac2e1c04a95e4fa4a0e63c81a3ee
--- /dev/null
+++ b/core/common/src/commonMain/kotlin/net/thunderbird/core/common/collections/Queue.kt
@@ -0,0 +1,96 @@
+package net.thunderbird.core.common.collections
+
+/**
+ * A collection designed for holding elements prior to processing.
+ * Besides basic [Collection] operations, queues provide additional insertion, extraction, and inspection operations.
+ *
+ * Queues typically, but do not necessarily, order elements in a FIFO (first-in-first-out) manner.
+ */
+interface Queue : Collection {
+ /**
+ * Inserts the specified element into this queue.
+ *
+ * @return `true` if the element was added to this queue.
+ * @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions.
+ */
+ fun add(element: T): Boolean
+
+ /**
+ * Inserts the specified element into this queue.
+ *
+ * @return `true` if the element was added to this queue, else `false`.
+ */
+ fun offer(element: T): Boolean
+
+ /**
+ * Retrieves and removes the head of this queue.
+ *
+ * @return the head of this queue.
+ * @throws NoSuchElementException if this queue is empty.
+ */
+ fun remove(): T
+
+ /**
+ * @return Retrieves and removes the head of this queue, or returns `null` if this queue is empty.
+ */
+ fun poll(): T?
+
+ /**
+ * Retrieves, but does not remove, the head of this queue.
+ *
+ * @return the head of this queue.
+ * @throws NoSuchElementException if this queue is empty.
+ */
+ fun element(): T
+
+ /**
+ * @return Retrieves, but does not remove, the head of this queue, or returns `null` if this queue is empty.
+ */
+ fun peek(): T?
+}
+
+abstract class AbstractQueue : AbstractMutableCollection(), Queue {
+
+ override fun element(): T {
+ val head = peek()
+ if (head != null) {
+ return head
+ } else {
+ error("Collection empty")
+ }
+ }
+
+ override fun add(element: T): Boolean {
+ if (offer(element)) {
+ return true
+ } else {
+ error("Collection full")
+ }
+ }
+
+ override fun addAll(elements: Collection): Boolean {
+ require(elements != this) { "Collection can't addAll itself" }
+ var modified = false
+ for (element in elements) {
+ if (add(element)) {
+ modified = true
+ }
+ }
+ return modified
+ }
+
+ override fun remove(): T {
+ val head = poll()
+ if (head != null) {
+ return head
+ } else {
+ throw NoSuchElementException("Queue is empty")
+ }
+ }
+
+ override fun clear() {
+ while (!isEmpty()) {
+ remove()
+ }
+ }
+}
diff --git a/core/common/src/commonMain/kotlin/net/thunderbird/core/common/domain/usecase/validation/ValidationError.kt b/core/common/src/commonMain/kotlin/net/thunderbird/core/common/domain/usecase/validation/ValidationError.kt
deleted file mode 100644
index 2cd21eebf5bd7f81f3fbcc9ac5b61c1fb5970ca5..0000000000000000000000000000000000000000
--- a/core/common/src/commonMain/kotlin/net/thunderbird/core/common/domain/usecase/validation/ValidationError.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package net.thunderbird.core.common.domain.usecase.validation
-
-interface ValidationError
diff --git a/core/common/src/commonMain/kotlin/net/thunderbird/core/common/domain/usecase/validation/ValidationResult.kt b/core/common/src/commonMain/kotlin/net/thunderbird/core/common/domain/usecase/validation/ValidationResult.kt
deleted file mode 100644
index 8a80cf163ad108569266484f937f480edf510879..0000000000000000000000000000000000000000
--- a/core/common/src/commonMain/kotlin/net/thunderbird/core/common/domain/usecase/validation/ValidationResult.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.thunderbird.core.common.domain.usecase.validation
-
-sealed interface ValidationResult {
- data object Success : ValidationResult
-
- data class Failure(val error: ValidationError) : ValidationResult
-}
diff --git a/core/common/src/commonTest/kotlin/net/thunderbird/core/common/collections/PriorityQueueTests.kt b/core/common/src/commonTest/kotlin/net/thunderbird/core/common/collections/PriorityQueueTests.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ba368905586449f9d4a995543bfbb366ad4cd7c6
--- /dev/null
+++ b/core/common/src/commonTest/kotlin/net/thunderbird/core/common/collections/PriorityQueueTests.kt
@@ -0,0 +1,272 @@
+package net.thunderbird.core.common.collections
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import assertk.assertions.isFalse
+import assertk.assertions.isNull
+import assertk.assertions.isTrue
+import kotlin.test.Test
+
+class PriorityQueueTests {
+ @Test
+ fun `minPriorityQueueOf - isEmpty returns true for new queue`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+
+ // Act
+ val isEmpty = queue.isEmpty()
+
+ // Assert
+ assertThat(isEmpty).isTrue()
+ }
+
+ @Test
+ fun `isEmpty returns false for non-empty queue`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+ queue.add(1)
+
+ // Act
+ val isEmpty = queue.isEmpty()
+
+ // Assert
+ assertThat(isEmpty).isFalse()
+ }
+
+ @Test
+ fun `minPriorityQueueOf - size returns correct number of elements`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+
+ // Pre-Act Assert
+ assertThat(queue.size).isEqualTo(0)
+
+ // Act
+ queue.add("A")
+
+ // Assert
+ assertThat(queue.size).isEqualTo(1)
+
+ // Act (Phase 2)
+ queue.add("B")
+
+ // Assert (Phase 2)
+ assertThat(queue.size).isEqualTo(2)
+
+ // Act (Phase 3)
+ queue.poll()
+
+ // Assert (Phase 3)
+ assertThat(queue.size).isEqualTo(1)
+ }
+
+ @Test
+ fun `minPriorityQueueOf - poll returns null for empty queue`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+
+ // Act
+ val element = queue.poll()
+
+ // Assert
+ assertThat(element).isNull()
+ }
+
+ @Test
+ fun `minPriorityQueueOf - peek returns null for empty queue`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+
+ // Act
+ val element = queue.peek()
+
+ // Assert
+ assertThat(element).isNull()
+ }
+
+ @Test
+ fun `minPriorityQueueOf - peek returns the smallest element without removing it`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+ queue.add('C')
+ queue.add('A')
+ queue.add('B')
+
+ // Act
+ val element1 = queue.peek()
+ val element2 = queue.peek()
+
+ // Assert
+ assertThat(queue.size).isEqualTo(3)
+ assertThat(element1).isEqualTo('A')
+ assertThat(element2).isEqualTo('A') // Subsequent peeks return the same element
+ assertThat(queue.size).isEqualTo(3)
+ }
+
+ @Test
+ fun `minPriorityQueueOf - add and poll elements in natural order`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+ queue.add(5)
+ queue.add(1)
+ queue.add(10)
+ queue.add(3)
+
+ // Act & Assert
+ assertThat(queue.poll()).isEqualTo(1)
+ assertThat(queue.poll()).isEqualTo(3)
+ assertThat(queue.poll()).isEqualTo(5)
+ assertThat(queue.poll()).isEqualTo(10)
+ assertThat(queue.poll()).isNull()
+ }
+
+ @Test
+ fun `minPriorityQueueOf - clear removes all elements from the queue`() {
+ // Arrange
+ val queue = minPriorityQueueOf()
+ queue.add(10)
+ queue.add(20)
+ queue.add(5)
+
+ // Act
+ queue.clear()
+
+ // Assert
+ assertThat(queue.isEmpty()).isTrue()
+ assertThat(queue.size).isEqualTo(0)
+ assertThat(queue.peek()).isNull()
+ assertThat(queue.poll()).isNull()
+ }
+
+ // maxPriorityQueueOf tests
+ @Test
+ fun `maxPriorityQueueOf - isEmpty returns true for new queue`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+
+ // Act
+ val isEmpty = queue.isEmpty()
+
+ // Assert
+ assertThat(isEmpty).isTrue()
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - isEmpty returns false for non-empty queue`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+ queue.add(1)
+
+ // Act
+ val isEmpty = queue.isEmpty()
+
+ // Assert
+ assertThat(isEmpty).isFalse()
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - size returns correct number of elements`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+
+ // Pre-Act Assert
+ assertThat(queue.size).isEqualTo(0)
+
+ // Act
+ queue.add("A")
+
+ // Assert
+ assertThat(queue.size).isEqualTo(1)
+
+ // Act (Phase 2)
+ queue.add("B")
+
+ // Assert (Phase 2)
+ assertThat(queue.size).isEqualTo(2)
+
+ // Act (Phase 3)
+ queue.poll()
+
+ // Assert (Phase 3)
+ assertThat(queue.size).isEqualTo(1)
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - poll returns null for empty queue`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+
+ // Act
+ val element = queue.poll()
+
+ // Assert
+ assertThat(element).isNull()
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - peek returns null for empty queue`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+
+ // Act
+ val element = queue.peek()
+
+ // Assert
+ assertThat(element).isNull()
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - peek returns the largest element without removing it`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+ queue.add('C')
+ queue.add('A')
+ queue.add('D')
+ queue.add('B')
+
+ // Act
+ val element1 = queue.peek()
+ val element2 = queue.peek()
+
+ // Assert
+ assertThat(queue.size).isEqualTo(4)
+ assertThat(element1).isEqualTo('D')
+ assertThat(element2).isEqualTo('D') // Subsequent peeks return the same element
+ assertThat(queue.size).isEqualTo(4)
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - add and poll elements in reverse natural order`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+ queue.add(5)
+ queue.add(1)
+ queue.add(10)
+ queue.add(3)
+
+ // Act & Assert
+ assertThat(queue.poll()).isEqualTo(10)
+ assertThat(queue.poll()).isEqualTo(5)
+ assertThat(queue.poll()).isEqualTo(3)
+ assertThat(queue.poll()).isEqualTo(1)
+ assertThat(queue.poll()).isNull()
+ }
+
+ @Test
+ fun `maxPriorityQueueOf - clear removes all elements from the queue`() {
+ // Arrange
+ val queue = maxPriorityQueueOf()
+ queue.add(10)
+ queue.add(20)
+ queue.add(5)
+
+ // Act
+ queue.clear()
+
+ // Assert
+ assertThat(queue.isEmpty()).isTrue()
+ assertThat(queue.size).isEqualTo(0)
+ assertThat(queue.peek()).isNull()
+ assertThat(queue.poll()).isNull()
+ }
+}
diff --git a/core/file/README.md b/core/file/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a670d70c1f84589e2450e16261b746070b7f4175
--- /dev/null
+++ b/core/file/README.md
@@ -0,0 +1,120 @@
+# Thunderbird Core File Module
+
+This module provides a simple, consistent API for common file operations across Android and JVM platforms.
+
+## Architecture
+
+The file system layer is split into two levels:
+
+- Public low-level I/O: `FileSystemManager` opens `RawSource`/`RawSink` for a given `Uri`.
+ - Android actual: `AndroidFileSystemManager`
+ - JVM actual: `JvmFileSystemManager`
+- Public high-level facade: `FileManager` for common operations (currently: copy).
+ - Default implementation: `DefaultFileManager` delegating to internal commands
+- Internal commands: e.g., `CopyCommand(source, dest)` implement operations using `FileSystemManager`.
+ - Hidden from public API; return `Outcome` internally to preserve error context.
+- `RawSource`/`RawSink` come from `kotlinx-io` and are referenced in the public API.
+
+### Core Components
+
+```mermaid
+classDiagram
+ class FileManager {
+ +copy(source: Uri, dest: Uri): Outcome
+ }
+
+ class DefaultFileManager {
+ -fs: FileSystemManager
+ }
+
+ class FileSystemManager {
+ +openSource(uri: Uri): RawSource?
+ +openSink(uri: Uri, mode: WriteMode = WriteMode.Truncate): RawSink?
+ }
+
+ class CopyCommand {
+ -source: Uri
+ -destination: Uri
+ +invoke(fs: FileSystemManager): Outcome
+ }
+
+ class FileOperationError {
+ }
+
+ DefaultFileManager ..> FileSystemManager
+ CopyCommand --> FileSystemManager
+ DefaultFileManager ..> CopyCommand : delegates
+```
+
+## Getting Started
+
+### Dependency setup
+
+Add the module to your Gradle build. Then, depending on your platform, provide an actual `FileSystemManager` and wire a `FileManager`:
+
+```kotlin
+// Koin example (Android)
+single { AndroidFileSystemManager(androidContext().contentResolver) }
+single { DefaultFileManager(get()) }
+```
+
+For JVM-only tools/tests:
+
+```kotlin
+val fs: FileSystemManager = JvmFileSystemManager()
+val fileManager: FileManager = DefaultFileManager(fs)
+```
+
+## Public API
+
+- FileManager
+ - `suspend fun copy(sourceUri: Uri, destinationUri: Uri): Outcome`
+- FileSystemManager
+ - `fun openSource(uri: Uri): RawSource?`
+ - `fun openSink(uri: Uri, mode: WriteMode = WriteMode.Truncate): RawSink?`
+ - Behavior:
+ - Sinks default to overwrite/truncate. Pass `WriteMode.Append` to append where supported.
+ - Returns null when the URI cannot be opened (e.g., missing permissions, unsupported scheme).
+ - Thread-safety: Implementations are stateless and safe to use from multiple threads, but the returned streams must be used/closed by the caller.
+- `enum class WriteMode { Truncate, Append }`
+
+## URI type
+
+The API uses a KMP‑friendly `Uri` type (com.eygraber.uri.Uri). On Android, convert a platform URI using the provided extension:
+
+```kotlin
+val kmpUri = androidUri.toKmpUri()
+```
+
+To build URIs in tests or common code, you can parse a string:
+
+```kotlin
+val source = "file:///path/to/file.txt".toKmpUri()
+```
+
+## Supported URIs (by platform)
+
+- Android (AndroidFileSystemManager):
+ - `content://` via `ContentResolver`
+ - `file://` via `ContentResolver`
+- JVM (JvmFileSystemManager):
+ - `file://` URIs only (non-`file:` schemes are not supported and will return null).
+- iOS: No actual yet in this repository, but the API is compatible. An iOS actual can use `NSFileManager`/`NSURL`.
+
+## Error handling best practices
+
+- `openSource(uri)`/`openSink(uri)` return null on failure. Always check for null and handle gracefully (e.g., show a message, request permissions).
+- On Android, failures are frequently due to missing URI permissions; prefer SAF pickers and persist permissions when needed.
+
+## Performance and buffering
+
+- Internal copy uses a buffered loop (`BUFFER_SIZE = 8_192L`).
+- Streams are flushed and closed to avoid leaks.
+- Public `openSource`/`openSink` are not suspending; perform I/O on an appropriate dispatcher/thread when needed.
+
+## Limitations and notes
+
+- Android: Ensure the app holds read/write permissions for the target URI (e.g., via SAF and optionally `takePersistableUriPermission`).
+- JVM: Only `file:` URIs are supported by `JvmFileSystemManager`.
+- iOS: No actual yet. The public API is prepared for an iOS actual using `NSFileManager`/`NSURL`.
+
diff --git a/core/file/build.gradle.kts b/core/file/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..ee369256b9f1652c81babeec7642834d4ad7dd38
--- /dev/null
+++ b/core/file/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ id(ThunderbirdPlugins.Library.kmp)
+}
+
+android {
+ namespace = "net.thunderbird.core.file"
+}
+
+kotlin {
+ sourceSets {
+ commonMain.dependencies {
+ api(libs.uri)
+
+ implementation(projects.core.outcome)
+
+ implementation(libs.kotlinx.io.core)
+ }
+ androidUnitTest.dependencies {
+ implementation(libs.robolectric)
+ }
+ }
+}
diff --git a/core/file/src/androidMain/kotlin/net/thunderbird/core/file/AndroidFileSystemManager.kt b/core/file/src/androidMain/kotlin/net/thunderbird/core/file/AndroidFileSystemManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..613d1820e4920fec025a16cdad9fc279aa774ff8
--- /dev/null
+++ b/core/file/src/androidMain/kotlin/net/thunderbird/core/file/AndroidFileSystemManager.kt
@@ -0,0 +1,29 @@
+package net.thunderbird.core.file
+
+import android.content.ContentResolver
+import com.eygraber.uri.Uri
+import com.eygraber.uri.toAndroidUri
+import kotlinx.io.RawSink
+import kotlinx.io.RawSource
+import kotlinx.io.asSink
+import kotlinx.io.asSource
+
+/**
+ * Android implementation of [FileSystemManager] that uses [ContentResolver] to perform file operations.
+ */
+class AndroidFileSystemManager(
+ private val contentResolver: ContentResolver,
+) : FileSystemManager {
+ override fun openSink(uri: Uri, mode: WriteMode): RawSink? {
+ // Map WriteMode to ContentResolver open modes: "wt" (truncate) or "wa" (append)
+ val androidMode = when (mode) {
+ WriteMode.Truncate -> "wt"
+ WriteMode.Append -> "wa"
+ }
+ return contentResolver.openOutputStream(uri.toAndroidUri(), androidMode)?.asSink()
+ }
+
+ override fun openSource(uri: Uri): RawSource? {
+ return contentResolver.openInputStream(uri.toAndroidUri())?.asSource()
+ }
+}
diff --git a/core/file/src/androidUnitTest/kotlin/net/thunderbird/core/file/AndroidFileSystemManagerTest.kt b/core/file/src/androidUnitTest/kotlin/net/thunderbird/core/file/AndroidFileSystemManagerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..60113b0eda70a1e6ffa4a551baeceb9a8472e642
--- /dev/null
+++ b/core/file/src/androidUnitTest/kotlin/net/thunderbird/core/file/AndroidFileSystemManagerTest.kt
@@ -0,0 +1,134 @@
+package net.thunderbird.core.file
+
+import android.content.Context
+import android.net.Uri
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import com.eygraber.uri.toKmpUri
+import kotlinx.io.Buffer
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(manifest = Config.NONE)
+class AndroidFileSystemManagerTest {
+
+ private val appContext: Context = RuntimeEnvironment.getApplication()
+
+ private val testSubject = AndroidFileSystemManager(appContext.contentResolver)
+
+ @JvmField
+ @Rule
+ val folder = TemporaryFolder()
+
+ @Test
+ fun openSinkAndOpenSource_writeAndReadFileContentRoundtrip() {
+ // Arrange
+ val tempFile = folder.newFile("tb-file-fs-test-android.txt")
+ val uri: Uri = Uri.fromFile(tempFile)
+ val testText = "Hello Thunderbird Android!"
+
+ // Act
+ val sink = checkNotNull(testSubject.openSink(uri.toKmpUri()))
+ val writeBuffer = Buffer().apply { write(testText.encodeToByteArray()) }
+ sink.write(writeBuffer, writeBuffer.size)
+ sink.flush()
+ sink.close()
+
+ val source = checkNotNull(testSubject.openSource(uri.toKmpUri()))
+ val readBuffer = Buffer()
+ source.readAtMostTo(readBuffer, 1024)
+ val bytes = ByteArray(readBuffer.size.toInt())
+ for (i in bytes.indices) {
+ bytes[i] = readBuffer.readByte()
+ }
+ val result = bytes.decodeToString()
+ source.close()
+
+ // Assert
+ assertThat(result).isEqualTo(testText)
+ }
+
+ @Test
+ fun openSink_withAppend_shouldAppendToExistingContent() {
+ // Arrange
+ val tempFile = folder.newFile("tb-file-fs-test-android-append.txt")
+ val uri: Uri = Uri.fromFile(tempFile)
+ val initial = "Hello"
+ val extra = " Android"
+
+ // Write initial content (truncate by default)
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri.toKmpUri()))
+ val buf = Buffer().apply { write(initial.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Append extra content
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri.toKmpUri(), WriteMode.Append))
+ val buf = Buffer().apply { write(extra.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Read back
+ val source = checkNotNull(testSubject.openSource(uri.toKmpUri()))
+ val readBuffer = Buffer()
+ source.readAtMostTo(readBuffer, 1024)
+ val bytes = ByteArray(readBuffer.size.toInt())
+ repeat(bytes.size) { i -> bytes[i] = readBuffer.readByte() }
+ val result = bytes.decodeToString()
+ source.close()
+
+ // Assert
+ assertThat(result).isEqualTo(initial + extra)
+ }
+
+ @Test
+ fun openSink_withTruncate_shouldOverwriteExistingContent() {
+ // Arrange
+ val tempFile = folder.newFile("tb-file-fs-test-android-truncate.txt")
+ val uri: Uri = Uri.fromFile(tempFile)
+ val first = "First"
+ val second = "Second"
+
+ // Write first content
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri.toKmpUri(), WriteMode.Truncate))
+ val buf = Buffer().apply { write(first.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Overwrite with second content
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri.toKmpUri(), WriteMode.Truncate))
+ val buf = Buffer().apply { write(second.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Read back
+ val source = checkNotNull(testSubject.openSource(uri.toKmpUri()))
+ val readBuffer = Buffer()
+ source.readAtMostTo(readBuffer, 1024)
+ val bytes = ByteArray(readBuffer.size.toInt())
+ repeat(bytes.size) { i -> bytes[i] = readBuffer.readByte() }
+ val result = bytes.decodeToString()
+ source.close()
+
+ // Assert
+ assertThat(result).isEqualTo(second)
+ }
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/DefaultFileManager.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/DefaultFileManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..700371bb4454e7fe9817806363451394973c7a21
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/DefaultFileManager.kt
@@ -0,0 +1,15 @@
+package net.thunderbird.core.file
+
+import com.eygraber.uri.Uri
+import net.thunderbird.core.file.command.CopyCommand
+import net.thunderbird.core.outcome.Outcome
+
+/**
+ * Default implementation that delegates to internal commands.
+ */
+class DefaultFileManager(
+ private val fileSystemManager: FileSystemManager,
+) : FileManager {
+ override suspend fun copy(sourceUri: Uri, destinationUri: Uri): Outcome =
+ CopyCommand(sourceUri, destinationUri).invoke(fileSystemManager)
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileManager.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4f214653ef6eeeced40b37f71f0fa68b5df9cded
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileManager.kt
@@ -0,0 +1,18 @@
+package net.thunderbird.core.file
+
+import com.eygraber.uri.Uri
+import net.thunderbird.core.outcome.Outcome
+
+/**
+ * File manager for common file operations.
+ */
+interface FileManager {
+ /**
+ * Copy data from [sourceUri] to [destinationUri].
+ *
+ * @param sourceUri The [Uri] of the source file.
+ * @param destinationUri The [Uri] of the destination file.
+ * @return [Outcome] with [Unit] on success or [FileOperationError] on failure.
+ */
+ suspend fun copy(sourceUri: Uri, destinationUri: Uri): Outcome
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileOperationError.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileOperationError.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6b15604d36fe024b91dc34a24b445c772546c266
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileOperationError.kt
@@ -0,0 +1,20 @@
+package net.thunderbird.core.file
+
+import com.eygraber.uri.Uri
+
+/**
+ * Common file operation errors.
+ */
+sealed interface FileOperationError {
+ /** Endpoint couldn't be opened or accessed. */
+ data class Unavailable(val uri: Uri, val message: String? = null) : FileOperationError
+
+ /** Failed while reading from the source. */
+ data class ReadFailed(val uri: Uri, val message: String? = null) : FileOperationError
+
+ /** Failed while writing to the destination. */
+ data class WriteFailed(val uri: Uri, val message: String? = null) : FileOperationError
+
+ /** Fallback when the error type can't be determined. */
+ data class Unknown(val message: String? = null) : FileOperationError
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileSystemManager.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileSystemManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f37f0fa3b5996de9b8cc9394a4abb9c03149d374
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/FileSystemManager.kt
@@ -0,0 +1,31 @@
+package net.thunderbird.core.file
+
+import com.eygraber.uri.Uri
+import kotlinx.io.RawSink
+import kotlinx.io.RawSource
+
+/**
+ * An interface for file system operations that are platform-specific.
+ */
+interface FileSystemManager {
+ /**
+ * Opens a sink for writing to a URI.
+ *
+ * Implementations must honor the requested [mode]:
+ * - [WriteMode.Truncate]: overwrite existing content (truncate) or create if missing
+ * - [WriteMode.Append]: append to existing content or create if missing
+ *
+ * @param uri The URI to open a sink for
+ * @param mode The write mode (truncate/append), defaults to [WriteMode.Truncate]
+ * @return A sink for writing to the URI, or null if the URI couldn't be opened
+ */
+ fun openSink(uri: Uri, mode: WriteMode = WriteMode.Truncate): RawSink?
+
+ /**
+ * Opens a source for reading from a URI.
+ *
+ * @param uri The URI to open a source for
+ * @return A source for reading from the URI, or null if the URI couldn't be opened
+ */
+ fun openSource(uri: Uri): RawSource?
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/WriteMode.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/WriteMode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1d6f0a7f6894f7847cb11cd249cf529a00aa0d47
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/WriteMode.kt
@@ -0,0 +1,12 @@
+package net.thunderbird.core.file
+
+/**
+ * Indicates how a sink should be opened for writing.
+ *
+ * - [Truncate]: Overwrite existing content or create if missing
+ * - [Append]: Append to existing content or create if missing
+ */
+enum class WriteMode {
+ Truncate,
+ Append,
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/command/CopyCommand.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/command/CopyCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bdfd624cf185883002eb9342802eed3f2885cd6c
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/command/CopyCommand.kt
@@ -0,0 +1,64 @@
+package net.thunderbird.core.file.command
+
+import com.eygraber.uri.Uri
+import kotlinx.io.Buffer
+import net.thunderbird.core.file.FileOperationError
+import net.thunderbird.core.file.FileSystemManager
+import net.thunderbird.core.file.WriteMode
+import net.thunderbird.core.outcome.Outcome
+
+/**
+ * Copies data from [sourceUri] to [destinationUri] using buffered I/O.
+ */
+internal class CopyCommand(
+ private val sourceUri: Uri,
+ private val destinationUri: Uri,
+) : FileCommand {
+ override suspend fun invoke(fs: FileSystemManager): Outcome {
+ // Open endpoints
+ val source = fs.openSource(sourceUri)
+ ?: return Outcome.Failure(
+ FileOperationError.Unavailable(sourceUri, "Unable to open source: $sourceUri"),
+ )
+ val sink = fs.openSink(destinationUri, WriteMode.Truncate)
+ ?: return Outcome.Failure(
+ FileOperationError.Unavailable(destinationUri, "Unable to open destination: $destinationUri"),
+ )
+
+ return try {
+ val buffer = Buffer()
+ while (true) {
+ val read = try {
+ source.readAtMostTo(buffer, BUFFER_SIZE)
+ } catch (e: Exception) {
+ return Outcome.Failure(FileOperationError.ReadFailed(sourceUri, e.message), cause = e)
+ }
+ if (read <= 0L) break
+ try {
+ sink.write(buffer, read)
+ } catch (e: Exception) {
+ return Outcome.Failure(FileOperationError.WriteFailed(destinationUri, e.message), cause = e)
+ }
+ }
+ try {
+ sink.flush()
+ } catch (e: Exception) {
+ return Outcome.Failure(FileOperationError.WriteFailed(destinationUri, e.message), cause = e)
+ }
+ Outcome.Success(Unit)
+ } catch (e: Exception) {
+ Outcome.Failure(FileOperationError.Unknown(e.message), cause = e)
+ } finally {
+ try {
+ source.close()
+ } catch (_: Exception) {}
+ try {
+ sink.close()
+ } catch (_: Exception) {}
+ }
+ }
+
+ private companion object {
+ const val BUFFER_SIZE = 8_192L
+ }
+}
diff --git a/core/file/src/commonMain/kotlin/net/thunderbird/core/file/command/FileCommand.kt b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/command/FileCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..69f26cfb6f63f875e997019712a623db79af4def
--- /dev/null
+++ b/core/file/src/commonMain/kotlin/net/thunderbird/core/file/command/FileCommand.kt
@@ -0,0 +1,14 @@
+package net.thunderbird.core.file.command
+
+import net.thunderbird.core.file.FileOperationError
+import net.thunderbird.core.file.FileSystemManager
+import net.thunderbird.core.outcome.Outcome
+
+/**
+ * A command that performs a file operation using the provided [FileSystemManager].
+ *
+ * @param T The type of the result produced by the command.
+ */
+internal fun interface FileCommand {
+ suspend operator fun invoke(fs: FileSystemManager): Outcome
+}
diff --git a/core/file/src/commonTest/kotlin/net/thunderbird/core/file/FakeFileSystemManager.kt b/core/file/src/commonTest/kotlin/net/thunderbird/core/file/FakeFileSystemManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ff61e3f50bb7c3b6d5c116cd1056312f4b3e19aa
--- /dev/null
+++ b/core/file/src/commonTest/kotlin/net/thunderbird/core/file/FakeFileSystemManager.kt
@@ -0,0 +1,68 @@
+package net.thunderbird.core.file
+
+import com.eygraber.uri.Uri
+import kotlinx.io.Buffer
+import kotlinx.io.RawSink
+import kotlinx.io.RawSource
+
+/**
+ * In-memory fake implementation of FileSystemManager for common tests.
+ * Stores data in a simple map keyed by URI string.
+ */
+class FakeFileSystemManager : FileSystemManager {
+
+ private val storage = mutableMapOf()
+
+ override fun openSink(uri: Uri, mode: WriteMode): RawSink? {
+ val key = uri.toString()
+ return object : RawSink {
+ private val collected = mutableListOf().apply {
+ if (mode == WriteMode.Append) {
+ storage[key]?.forEach { add(it) }
+ }
+ }
+
+ override fun write(source: Buffer, byteCount: Long) {
+ // Read exactly byteCount bytes from source and collect
+ val count = byteCount.toInt()
+ repeat(count) {
+ if (source.size <= 0L) return
+ collected += source.readByte()
+ }
+ }
+
+ override fun flush() {
+ storage[key] = collected.toByteArray()
+ }
+
+ override fun close() {
+ // ensure data is stored
+ flush()
+ }
+ }
+ }
+
+ override fun openSource(uri: Uri): RawSource? {
+ val key = uri.toString()
+ val bytes = storage[key] ?: return null
+ return object : RawSource {
+ private val buffer = Buffer().apply { write(bytes) }
+ override fun readAtMostTo(sink: Buffer, byteCount: Long): Long {
+ val toRead = minOf(byteCount, buffer.size)
+ if (toRead <= 0L) return 0L
+ sink.write(buffer, toRead)
+ return toRead
+ }
+
+ override fun close() {
+ // no-op
+ }
+ }
+ }
+
+ fun put(uriString: String, content: ByteArray) {
+ storage[uriString] = content
+ }
+
+ fun get(uriString: String): ByteArray? = storage[uriString]
+}
diff --git a/core/file/src/commonTest/kotlin/net/thunderbird/core/file/command/CopyCommandTest.kt b/core/file/src/commonTest/kotlin/net/thunderbird/core/file/command/CopyCommandTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..79b3e68594e774842ef5cb04a728cdc3101627ad
--- /dev/null
+++ b/core/file/src/commonTest/kotlin/net/thunderbird/core/file/command/CopyCommandTest.kt
@@ -0,0 +1,44 @@
+package net.thunderbird.core.file.command
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import com.eygraber.uri.toKmpUri
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import net.thunderbird.core.file.FakeFileSystemManager
+import net.thunderbird.core.file.FileOperationError
+import net.thunderbird.core.outcome.Outcome
+
+class CopyCommandTest {
+
+ private val fs = FakeFileSystemManager()
+ private val testSubject = CopyCommand(
+ sourceUri = "mem://source".toKmpUri(),
+ destinationUri = "mem://dest".toKmpUri(),
+ )
+
+ @Test
+ fun `execute should copy bytes from source to destination`() = runTest {
+ // Arrange
+ val content = "Thunderbird common copy test".encodeToByteArray()
+ fs.put("mem://source", content)
+
+ // Act
+ val result = testSubject(fs)
+
+ // Assert
+ assertThat(result.isSuccess).isEqualTo(true)
+ assertThat(fs.get("mem://dest")?.decodeToString()).isEqualTo("Thunderbird common copy test")
+ }
+
+ @Test
+ fun `execute should fail when source cannot be opened`() = runTest {
+ // Arrange - no source preloaded
+
+ // Act
+ val result = testSubject(fs)
+
+ // Assert
+ assertThat(result is Outcome.Failure).isEqualTo(true)
+ }
+}
diff --git a/core/file/src/jvmMain/kotlin/net/thunderbird/core/file/JvmFileSystemManager.kt b/core/file/src/jvmMain/kotlin/net/thunderbird/core/file/JvmFileSystemManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e5e2e3ce58f774052f9608ffaa375479bba68f8f
--- /dev/null
+++ b/core/file/src/jvmMain/kotlin/net/thunderbird/core/file/JvmFileSystemManager.kt
@@ -0,0 +1,41 @@
+package net.thunderbird.core.file
+
+import com.eygraber.uri.Uri
+import com.eygraber.uri.toURI
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import kotlinx.io.RawSink
+import kotlinx.io.RawSource
+import kotlinx.io.asSink
+import kotlinx.io.asSource
+
+/**
+ * JVM implementation of [FileSystemManager] using java.io streams.
+ */
+class JvmFileSystemManager : FileSystemManager {
+ override fun openSink(uri: Uri, mode: WriteMode): RawSink? {
+ // Only support simple file paths for JVM implementation
+ return try {
+ val file = File(uri.toURI())
+ // create parent directories if necessary
+ file.parentFile?.mkdirs()
+ val append = when (mode) {
+ WriteMode.Truncate -> false
+ WriteMode.Append -> true
+ }
+ FileOutputStream(file, append).asSink()
+ } catch (_: Throwable) {
+ null
+ }
+ }
+
+ override fun openSource(uri: Uri): RawSource? {
+ return try {
+ val file = File(uri.toURI())
+ FileInputStream(file).asSource()
+ } catch (_: Throwable) {
+ null
+ }
+ }
+}
diff --git a/core/file/src/jvmTest/kotlin/net/thunderbird/core/file/JvmFileSystemManagerTest.kt b/core/file/src/jvmTest/kotlin/net/thunderbird/core/file/JvmFileSystemManagerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..596272f21fd117aa37d51515696c96fb7b683633
--- /dev/null
+++ b/core/file/src/jvmTest/kotlin/net/thunderbird/core/file/JvmFileSystemManagerTest.kt
@@ -0,0 +1,125 @@
+package net.thunderbird.core.file
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import com.eygraber.uri.Uri
+import java.io.File
+import kotlinx.io.Buffer
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class JvmFileSystemManagerTest {
+
+ private val testSubject = JvmFileSystemManager()
+
+ @JvmField
+ @Rule
+ val folder = TemporaryFolder()
+
+ @Test
+ fun `openSink and openSource should write and read file content roundtrip`() {
+ // Arrange
+ val tempFile: File = folder.newFile("tb-file-fs-test.txt")
+ val testText = "Hello Thunderbird!"
+ val uri = Uri.parse(tempFile.toURI().toString())
+ val sink = checkNotNull(testSubject.openSink(uri))
+
+ // Act
+ val writeBuffer = Buffer().apply { write(testText.encodeToByteArray()) }
+ sink.write(writeBuffer, writeBuffer.size)
+ sink.flush()
+ sink.close()
+
+ val source = checkNotNull(testSubject.openSource(uri))
+ val readBuffer = Buffer()
+ source.readAtMostTo(readBuffer, 1024)
+ val bytes = ByteArray(readBuffer.size.toInt())
+ for (i in bytes.indices) {
+ bytes[i] = readBuffer.readByte()
+ }
+ val result = bytes.decodeToString()
+ source.close()
+
+ // Assert
+ assertThat(result).isEqualTo(testText)
+ }
+
+ @Test
+ fun `openSink with Append should append to existing content`() {
+ // Arrange
+ val tempFile: File = folder.newFile("tb-file-fs-append.txt")
+ val uri = Uri.parse(tempFile.toURI().toString())
+ val initial = "Hello"
+ val extra = " World"
+
+ // Write initial content (truncate by default)
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri))
+ val buf = Buffer().apply { write(initial.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Append extra content
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri, WriteMode.Append))
+ val buf = Buffer().apply { write(extra.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Read back
+ val source = checkNotNull(testSubject.openSource(uri))
+ val readBuffer = Buffer()
+ source.readAtMostTo(readBuffer, 1024)
+ val bytes = ByteArray(readBuffer.size.toInt())
+ repeat(bytes.size) { i -> bytes[i] = readBuffer.readByte() }
+ val result = bytes.decodeToString()
+ source.close()
+
+ // Assert
+ assertThat(result).isEqualTo(initial + extra)
+ }
+
+ @Test
+ fun `openSink with Truncate should overwrite existing content`() {
+ // Arrange
+ val tempFile: File = folder.newFile("tb-file-fs-truncate.txt")
+ val uri = Uri.parse(tempFile.toURI().toString())
+ val first = "First"
+ val second = "Second"
+
+ // Write first content
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri, WriteMode.Truncate))
+ val buf = Buffer().apply { write(first.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Overwrite with second content
+ run {
+ val sink = checkNotNull(testSubject.openSink(uri, WriteMode.Truncate))
+ val buf = Buffer().apply { write(second.encodeToByteArray()) }
+ sink.write(buf, buf.size)
+ sink.flush()
+ sink.close()
+ }
+
+ // Read back
+ val source = checkNotNull(testSubject.openSource(uri))
+ val readBuffer = Buffer()
+ source.readAtMostTo(readBuffer, 1024)
+ val bytes = ByteArray(readBuffer.size.toInt())
+ repeat(bytes.size) { i -> bytes[i] = readBuffer.readByte() }
+ val result = bytes.decodeToString()
+ source.close()
+
+ // Assert
+ assertThat(result).isEqualTo(second)
+ }
+}
diff --git a/core/logging/impl-file/build.gradle.kts b/core/logging/impl-file/build.gradle.kts
index cc2a55ff65bde9f7d2fdcb2aaa2738c6f983e554..0a9c444bf11a62d3535a4710666052e2d4642aaa 100644
--- a/core/logging/impl-file/build.gradle.kts
+++ b/core/logging/impl-file/build.gradle.kts
@@ -9,8 +9,15 @@ android {
kotlin {
sourceSets {
commonMain.dependencies {
- implementation(libs.kotlinx.io.core)
implementation(projects.core.logging.api)
+ implementation(projects.core.file)
+ implementation(projects.core.outcome)
+
+ implementation(libs.kotlinx.io.core)
+ implementation(libs.uri)
+ }
+ androidUnitTest.dependencies {
+ implementation(libs.robolectric)
}
}
}
diff --git a/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSink.kt b/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSink.kt
index df564228c954a18e6ce6a8c53561f984ca7311e9..7e4bc0fe1433d2b6dcefd1b4aed16821a78402cf 100644
--- a/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSink.kt
+++ b/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSink.kt
@@ -1,7 +1,9 @@
package net.thunderbird.core.logging.file
+import androidx.core.net.toUri
+import com.eygraber.uri.Uri
+import com.eygraber.uri.toKmpUri
import java.io.File
-import java.io.FileInputStream
import java.io.FileOutputStream
import kotlin.coroutines.CoroutineContext
import kotlin.time.ExperimentalTime
@@ -16,19 +18,19 @@ import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import kotlinx.io.Buffer
-import kotlinx.io.RawSink
import kotlinx.io.asSink
+import net.thunderbird.core.file.FileManager
import net.thunderbird.core.logging.LogEvent
import net.thunderbird.core.logging.LogLevel
+import net.thunderbird.core.outcome.Outcome
-private const val BUFFER_SIZE = 8192 // 8KB buffer size
private const val LOG_BUFFER_COUNT = 4
open class AndroidFileLogSink(
override val level: LogLevel,
fileName: String,
fileLocation: String,
- private val fileSystemManager: FileSystemManager,
+ private val fileManager: FileManager,
coroutineContext: CoroutineContext = Dispatchers.IO,
) : FileLogSink {
@@ -93,14 +95,18 @@ open class AndroidFileLogSink(
}
}
- override suspend fun export(uriString: String) {
+ override suspend fun export(uri: Uri) {
if (accumulatedLogs.isNotEmpty()) {
writeToLogFile()
}
- val sink = fileSystemManager.openSink(uriString, "wt")
- ?: error("Error opening contentUri for writing")
- copyInternalFileToExternal(sink)
+ val sourceUri = logFile.toUri().toKmpUri()
+ val result = fileManager.copy(sourceUri = sourceUri, destinationUri = uri)
+ if (result is Outcome.Failure) {
+ error(
+ "Error copying log to destination: ${result.error}",
+ )
+ }
// Clear the log file after export
val outputStream = FileOutputStream(logFile)
@@ -116,25 +122,4 @@ open class AndroidFileLogSink(
outputStream.close()
}
}
-
- private fun copyInternalFileToExternal(sink: RawSink) {
- val inputStream = FileInputStream(logFile)
-
- try {
- val buffer = Buffer()
- val byteArray = ByteArray(BUFFER_SIZE)
- var bytesRead: Int
-
- while (inputStream.read(byteArray).also { bytesRead = it } != -1) {
- buffer.write(byteArray, 0, bytesRead)
- sink.write(buffer, buffer.size)
- buffer.clear()
- }
-
- sink.flush()
- } finally {
- inputStream.close()
- sink.close()
- }
- }
}
diff --git a/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileSystemManager.kt b/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileSystemManager.kt
deleted file mode 100644
index fe61e2ec87c9129a480c7de59fb587e4e94fc92b..0000000000000000000000000000000000000000
--- a/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileSystemManager.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.thunderbird.core.logging.file
-
-import android.content.ContentResolver
-import android.net.Uri
-import androidx.core.net.toUri
-import kotlinx.io.RawSink
-import kotlinx.io.asSink
-
-/**
- * Android implementation of [FileSystemManager] that uses [ContentResolver] to perform file operations.
- */
-class AndroidFileSystemManager(
- private val contentResolver: ContentResolver,
-) : FileSystemManager {
- override fun openSink(uriString: String, mode: String): RawSink? {
- val uri: Uri = uriString.toUri()
- return contentResolver.openOutputStream(uri, mode)?.asSink()
- }
-}
diff --git a/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.android.kt b/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.android.kt
index dd131394a8fe273e5d82ef055490ce8e233bea46..2726c5c715e4439d70f0fe4014a7c69e08729b70 100644
--- a/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.android.kt
+++ b/core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.android.kt
@@ -1,12 +1,18 @@
package net.thunderbird.core.logging.file
+import net.thunderbird.core.file.FileManager
import net.thunderbird.core.logging.LogLevel
actual fun FileLogSink(
level: LogLevel,
fileName: String,
fileLocation: String,
- fileSystemManager: FileSystemManager,
+ fileManager: FileManager,
): FileLogSink {
- return AndroidFileLogSink(level, fileName, fileLocation, fileSystemManager)
+ return AndroidFileLogSink(
+ level = level,
+ fileName = fileName,
+ fileLocation = fileLocation,
+ fileManager = fileManager,
+ )
}
diff --git a/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSinkTest.android.kt b/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSinkTest.android.kt
index 60674583cfe92f2a38e22a865cdab134b7708f3e..9407c624ff6907e6412a11abc54017e3e761896e 100644
--- a/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSinkTest.android.kt
+++ b/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSinkTest.android.kt
@@ -3,6 +3,7 @@ package net.thunderbird.core.logging.file
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isNotNull
+import com.eygraber.uri.toKmpUri
import java.io.File
import kotlin.test.Test
import kotlin.time.ExperimentalTime
@@ -18,8 +19,13 @@ import net.thunderbird.core.logging.LogLevel
import org.junit.Before
import org.junit.Rule
import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+@Config(manifest = Config.NONE)
class AndroidFileLogSinkTest {
@JvmField
@@ -29,19 +35,19 @@ class AndroidFileLogSinkTest {
private val initialTimestamp = 1234567890L
private lateinit var logFile: File
private lateinit var fileLocation: String
- private lateinit var fileManager: FakeFileSystemManager
+ private lateinit var fileManager: FakeFileManager
private lateinit var testSubject: AndroidFileLogSink
@Before
fun setUp() {
fileLocation = folder.newFolder().absolutePath
logFile = File(fileLocation, "test_log.txt")
- fileManager = FakeFileSystemManager()
+ fileManager = FakeFileManager()
testSubject = AndroidFileLogSink(
level = LogLevel.INFO,
fileName = "test_log",
fileLocation = fileLocation,
- fileSystemManager = fileManager,
+ fileManager = fileManager,
coroutineContext = UnconfinedTestDispatcher(),
)
}
@@ -137,7 +143,7 @@ class AndroidFileLogSinkTest {
runBlocking {
// Act
testSubject.flushAndCloseBuffer()
- val exportUri = "content://test/export.txt"
+ val exportUri = "content://test/export.txt".toKmpUri()
testSubject.export(exportUri)
}
@@ -182,7 +188,7 @@ class AndroidFileLogSinkTest {
assertThat(logFile.readText())
.isEqualTo(logString1)
runBlocking {
- val exportUri = "content://test/export.txt"
+ val exportUri = "content://test/export.txt".toKmpUri()
testSubject.export(exportUri)
}
diff --git a/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/FakeFileManager.kt b/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/FakeFileManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ccdd21359cbfb4c7ffd44b2017b3227e2bc059cd
--- /dev/null
+++ b/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/FakeFileManager.kt
@@ -0,0 +1,35 @@
+package net.thunderbird.core.logging.file
+
+import com.eygraber.uri.Uri
+import com.eygraber.uri.toAndroidUri
+import java.io.File
+import net.thunderbird.core.file.FileManager
+import net.thunderbird.core.file.FileOperationError
+import net.thunderbird.core.outcome.Outcome
+
+/**
+ * Fake FileManager that captures content copied from a local file source URI.
+ */
+class FakeFileManager : FileManager {
+ var exportedContent: String? = null
+
+ override suspend fun copy(
+ sourceUri: Uri,
+ destinationUri: Uri,
+ ): Outcome {
+ return try {
+ val androidUri = sourceUri.toAndroidUri()
+ val content = when (androidUri.scheme) {
+ "file" -> {
+ val path = requireNotNull(androidUri.path) { "File URI without path: $androidUri" }
+ File(path).readText(Charsets.UTF_8)
+ }
+ else -> error("Unsupported scheme for FakeFileManager source: ${androidUri.scheme}")
+ }
+ exportedContent = content
+ Outcome.Success(Unit)
+ } catch (t: Throwable) {
+ Outcome.Failure(FileOperationError.Unknown(t.message), cause = t)
+ }
+ }
+}
diff --git a/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/FakeFileSystemManager.kt b/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/FakeFileSystemManager.kt
deleted file mode 100644
index cdd9e18a468c4105fafa0586eb8b0f3f5e219f2d..0000000000000000000000000000000000000000
--- a/core/logging/impl-file/src/androidUnitTest/kotlin/net/thunderbird/core/logging/file/FakeFileSystemManager.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package net.thunderbird.core.logging.file
-
-import java.io.ByteArrayOutputStream
-import java.nio.charset.StandardCharsets
-import kotlinx.io.Buffer
-import kotlinx.io.RawSink
-
-class FakeFileSystemManager : FileSystemManager {
-
- var exportedContent: String? = null
- private val outputStream = ByteArrayOutputStream()
-
- override fun openSink(uriString: String, mode: String): RawSink? {
- return object : RawSink {
- override fun write(source: Buffer, byteCount: Long) {
- val bytes = ByteArray(byteCount.toInt())
-
- for (i in 0 until byteCount.toInt()) {
- bytes[i] = source.readByte()
- }
-
- outputStream.write(bytes)
-
- exportedContent = String(outputStream.toByteArray(), StandardCharsets.UTF_8)
- }
-
- override fun flush() = Unit
-
- override fun close() = Unit
- }
- }
-}
diff --git a/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.kt b/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.kt
index 1a368dd26fd59c7181472a507f7aa67b811e05f1..9fca3b9dbe7df740d0f3e92abc45a359694a3b90 100644
--- a/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.kt
+++ b/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.kt
@@ -1,15 +1,17 @@
package net.thunderbird.core.logging.file
+import com.eygraber.uri.Uri
+import net.thunderbird.core.file.FileManager
import net.thunderbird.core.logging.LogLevel
import net.thunderbird.core.logging.LogSink
interface FileLogSink : LogSink {
/**
* Exports from the logging method to the requested external file
- * @param uriString The [String] for the URI to export the log to
+ * @param uri The [String] for the URI to export the log to
*
**/
- suspend fun export(uriString: String)
+ suspend fun export(uri: Uri)
/**
* On a crash or close, flushes buffer to file fo avoid log loss
@@ -26,11 +28,12 @@ interface FileLogSink : LogSink {
* @param level The minimum [LogLevel] for messages to be logged.
* @param fileName The [String] fileName to log to
* @param fileLocation The [String] fileLocation for the log file
- * @param fileSystemManager The [FileSystemManager] abstraction for opening the file stream
+ * @param fileManager The [FileManager] to handle file operations
+ * @return A [FileLogSink] instance for logging to a file.
*/
expect fun FileLogSink(
level: LogLevel,
fileName: String,
fileLocation: String,
- fileSystemManager: FileSystemManager,
+ fileManager: FileManager,
): FileLogSink
diff --git a/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileSystemManager.kt b/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileSystemManager.kt
deleted file mode 100644
index aaca95a03dce3c9725ddc1438dca3b12ad9f23a5..0000000000000000000000000000000000000000
--- a/core/logging/impl-file/src/commonMain/kotlin/net/thunderbird/core/logging/file/FileSystemManager.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.thunderbird.core.logging.file
-
-import kotlinx.io.RawSink
-
-/**
- * An interface for file system operations that are platform-specific.
- */
-interface FileSystemManager {
- /**
- * Opens a sink for writing to a URI.
- *
- * @param uriString The URI string to open a sink for
- * @param mode The mode to open the sink in (e.g., "wt" for write text)
- * @return A sink for writing to the URI, or null if the URI couldn't be opened
- */
- fun openSink(uriString: String, mode: String): RawSink?
-}
diff --git a/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.jvm.kt b/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.jvm.kt
index 2ceb50437deef3a1da3b490e90853d355b002ed2..ae960a83959ba834420a956513516bb914b08708 100644
--- a/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.jvm.kt
+++ b/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/FileLogSink.jvm.kt
@@ -1,20 +1,13 @@
package net.thunderbird.core.logging.file
+import net.thunderbird.core.file.FileManager
import net.thunderbird.core.logging.LogLevel
-/**
- * A [LogSink] implementation that logs messages to a specified internal file.
- *
- * This sink uses the platform-specific implementations to handle logging.
- *
- * @param level The minimum [LogLevel] for messages to be logged.
- * @param fileName The [String] fileName to log to
- */
actual fun FileLogSink(
level: LogLevel,
fileName: String,
fileLocation: String,
- fileSystemManager: FileSystemManager,
+ fileManager: FileManager,
): FileLogSink {
return JvmFileLogSink(level, fileName, fileLocation)
}
diff --git a/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileLogSink.kt b/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileLogSink.kt
index 49f27e7ce5f0fac56c301355ffa10deb420b8637..2114fb51a81fcc58230a22164913a627046f4655 100644
--- a/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileLogSink.kt
+++ b/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileLogSink.kt
@@ -1,5 +1,6 @@
package net.thunderbird.core.logging.file
+import com.eygraber.uri.Uri
import net.thunderbird.core.logging.LogEvent
import net.thunderbird.core.logging.LogLevel
@@ -14,7 +15,7 @@ internal class JvmFileLogSink(
event.throwable?.printStackTrace()
}
- override suspend fun export(uriString: String) {
+ override suspend fun export(uri: Uri) {
// TODO: Implementation https://github.com/thunderbird/thunderbird-android/issues/9435
}
diff --git a/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileSystemManager.kt b/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileSystemManager.kt
deleted file mode 100644
index fb3179fc358d15e72e0f3ffc98410820003c27d1..0000000000000000000000000000000000000000
--- a/core/logging/impl-file/src/jvmMain/kotlin/net/thunderbird/core/logging/file/JvmFileSystemManager.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package net.thunderbird.core.logging.file
-
-import kotlinx.io.RawSink
-
-/**
- * Android implementation of [FileSystemManager] that uses [ContentResolver] to perform file operations.
- */
-class JvmFileSystemManager() : FileSystemManager {
- override fun openSink(uriString: String, mode: String): RawSink? {
- // TODO: Implementation https://github.com/thunderbird/thunderbird-android/issues/9435
- return TODO("Provide the return value")
- }
-}
diff --git a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettings.kt b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettings.kt
index fd9060108de76b33eb7d40a982e113d6dddffa31..3dc3a52f897adb9346ff8e851fac47edafdd31ff 100644
--- a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettings.kt
+++ b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettings.kt
@@ -3,12 +3,13 @@ package net.thunderbird.core.preference.display.visualSettings
const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_NAME = false
const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_PICTURE = true
const val DISPLAY_SETTINGS_DEFAULT_IS_CHANGE_CONTACT_NAME_COLOR = true
-const val DISPLAY_SETTINGS_DEFAULT_IS_COLORIZE_MISSING_CONTACT_PICTURE = false
+const val DISPLAY_SETTINGS_DEFAULT_IS_COLORIZE_MISSING_CONTACT_PICTURE = true
const val DISPLAY_SETTINGS_DEFAULT_IS_USE_BACKGROUND_AS_INDICATOR = false
const val DISPLAY_SETTINGS_DEFAULT_IS_USE_MESSAGE_VIEW_FIXED_WIDTH_FONT = false
const val DISPLAY_SETTINGS_DEFAULT_IS_AUTO_FIT_WIDTH = true
const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION = true
const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CORRESPONDENT_NAMES = true
+const val DISPLAY_SETTINGS_DEFAULT_MESSAGE_LIST_PREVIEW_LINES = 2
data class DisplayVisualSettings(
val isShowAnimations: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION,
@@ -20,4 +21,5 @@ data class DisplayVisualSettings(
val isUseBackgroundAsUnreadIndicator: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_USE_BACKGROUND_AS_INDICATOR,
val isUseMessageViewFixedWidthFont: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_USE_MESSAGE_VIEW_FIXED_WIDTH_FONT,
val isAutoFitWidth: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_AUTO_FIT_WIDTH,
+ val messageListPreviewLines: Int = DISPLAY_SETTINGS_DEFAULT_MESSAGE_LIST_PREVIEW_LINES,
)
diff --git a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettingsPreferenceManager.kt b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettingsPreferenceManager.kt
index 01396769bd8f37b6dd88abe4c52bd537b6cf1948..22535fe0b3063f0f886c480cdb34de7eb5948edb 100644
--- a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettingsPreferenceManager.kt
+++ b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DisplayVisualSettingsPreferenceManager.kt
@@ -11,5 +11,6 @@ const val KEY_USE_BACKGROUND_AS_UNREAD_INDICATOR = "isUseBackgroundAsUnreadIndic
const val KEY_MESSAGE_VIEW_FIXED_WIDTH_FONT = "messageViewFixedWidthFont"
const val KEY_AUTO_FIT_WIDTH = "autofitWidth"
const val KEY_SHOW_CONTACT_PICTURE = "showContactPicture"
+const val KEY_MESSAGE_LIST_VIEW_PREVIEW_LINES = "messageListPreviewLines"
interface DisplayVisualSettingsPreferenceManager : PreferenceManager
diff --git a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreference.kt b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreference.kt
index 43cc2a6a873b2b4f84890e12c3c8a6625698c191..07b660b99468731dbdb6a5ab71fad0ae1e89f081 100644
--- a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreference.kt
+++ b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreference.kt
@@ -3,9 +3,12 @@ package net.thunderbird.core.preference.notification
const val NOTIFICATION_PREFERENCE_DEFAULT_IS_QUIET_TIME_ENABLED = false
const val NOTIFICATION_PREFERENCE_DEFAULT_QUIET_TIME_STARTS = "21:00"
const val NOTIFICATION_PREFERENCE_DEFAULT_QUIET_TIME_END = "7:00"
+const val NOTIFICATION_PREFERENCE_DEFAULT_IS_NOTIFICATION_DURING_QUIET_TIME_ENABLED = true
data class NotificationPreference(
val isQuietTimeEnabled: Boolean = NOTIFICATION_PREFERENCE_DEFAULT_IS_QUIET_TIME_ENABLED,
val quietTimeStarts: String = NOTIFICATION_PREFERENCE_DEFAULT_QUIET_TIME_STARTS,
val quietTimeEnds: String = NOTIFICATION_PREFERENCE_DEFAULT_QUIET_TIME_END,
+ val isNotificationDuringQuietTimeEnabled: Boolean =
+ NOTIFICATION_PREFERENCE_DEFAULT_IS_NOTIFICATION_DURING_QUIET_TIME_ENABLED,
)
diff --git a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreferenceManager.kt b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreferenceManager.kt
index e9a402a65846f4f399112202bad2a1b3e319eef3..b28e1ab536cce9002fbfb2313a4b5f669e1df1ca 100644
--- a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreferenceManager.kt
+++ b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/notification/NotificationPreferenceManager.kt
@@ -5,5 +5,6 @@ import net.thunderbird.core.preference.PreferenceManager
const val KEY_QUIET_TIME_ENDS = "quietTimeEnds"
const val KEY_QUIET_TIME_STARTS = "quietTimeStarts"
const val KEY_QUIET_TIME_ENABLED = "quietTimeEnabled"
+const val KEY_NOTIFICATION_DURING_QUIET_TIME_ENABLED = "notificationDuringQuietTimeEnabled"
interface NotificationPreferenceManager : PreferenceManager
diff --git a/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DefaultDisplayVisualSettingsPreferenceManager.kt b/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DefaultDisplayVisualSettingsPreferenceManager.kt
index 4f12c1498226a8c25c2f1811b6c42cde8b30bf41..836c70036d254e94a3c990086ed02fd03859abd4 100644
--- a/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DefaultDisplayVisualSettingsPreferenceManager.kt
+++ b/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/visualSettings/DefaultDisplayVisualSettingsPreferenceManager.kt
@@ -70,6 +70,10 @@ class DefaultDisplayVisualSettingsPreferenceManager(
KEY_SHOW_CONTACT_PICTURE,
DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_PICTURE,
),
+ messageListPreviewLines = storage.getInt(
+ KEY_MESSAGE_LIST_VIEW_PREVIEW_LINES,
+ DISPLAY_SETTINGS_DEFAULT_MESSAGE_LIST_PREVIEW_LINES,
+ ),
)
private fun writeConfig(config: DisplayVisualSettings) {
@@ -103,6 +107,10 @@ class DefaultDisplayVisualSettingsPreferenceManager(
KEY_SHOW_CORRESPONDENT_NAMES,
config.isShowCorrespondentNames,
)
+ storageEditor.putInt(
+ KEY_MESSAGE_LIST_VIEW_PREVIEW_LINES,
+ config.messageListPreviewLines,
+ )
storageEditor.commit().also { commited ->
logger.verbose(TAG) { "writeConfig: storageEditor.commit() resulted in: $commited" }
}
diff --git a/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/notification/DefaultNotificationPreferenceManager.kt b/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/notification/DefaultNotificationPreferenceManager.kt
index 9549c3af62d1596ee008921d20d0e4b3fa23ddf1..96121c53ab35c2885a6c0096cba6b33ad18f995e 100644
--- a/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/notification/DefaultNotificationPreferenceManager.kt
+++ b/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/notification/DefaultNotificationPreferenceManager.kt
@@ -38,6 +38,10 @@ class DefaultNotificationPreferenceManager(
key = KEY_QUIET_TIME_ENDS,
defValue = NOTIFICATION_PREFERENCE_DEFAULT_QUIET_TIME_END,
),
+ isNotificationDuringQuietTimeEnabled = storage.getBoolean(
+ key = KEY_NOTIFICATION_DURING_QUIET_TIME_ENABLED,
+ defValue = NOTIFICATION_PREFERENCE_DEFAULT_IS_NOTIFICATION_DURING_QUIET_TIME_ENABLED,
+ ),
),
)
@@ -54,6 +58,10 @@ class DefaultNotificationPreferenceManager(
KEY_QUIET_TIME_ENABLED,
config.isQuietTimeEnabled,
)
+ storageEditor.putBoolean(
+ KEY_NOTIFICATION_DURING_QUIET_TIME_ENABLED,
+ config.isNotificationDuringQuietTimeEnabled,
+ )
storageEditor.commit().also { commited ->
logger.verbose(TAG) { "writeConfig: storageEditor.commit() resulted in: $commited" }
}
diff --git a/core/ui/compose/common/build.gradle.kts b/core/ui/compose/common/build.gradle.kts
index c64b04956f28af7ec64b4688c272b45f6577e74a..931e1048718a25839a46b986f56e26a848c66a40 100644
--- a/core/ui/compose/common/build.gradle.kts
+++ b/core/ui/compose/common/build.gradle.kts
@@ -5,6 +5,12 @@ plugins {
android {
namespace = "app.k9mail.core.ui.compose.common"
resourcePrefix = "core_ui_common_"
+
+ testOptions {
+ unitTests {
+ isIncludeAndroidResources = true
+ }
+ }
}
dependencies {
diff --git a/core/ui/compose/designsystem/build.gradle.kts b/core/ui/compose/designsystem/build.gradle.kts
index 664748c56e1548b8437a9f0c106dac9ede576474..5a4b42306a492a7167cb17f08a4721e67acc3a35 100644
--- a/core/ui/compose/designsystem/build.gradle.kts
+++ b/core/ui/compose/designsystem/build.gradle.kts
@@ -6,6 +6,12 @@ plugins {
android {
namespace = "app.k9mail.core.ui.compose.designsystem"
resourcePrefix = "designsystem_"
+
+ testOptions {
+ unitTests {
+ isIncludeAndroidResources = true
+ }
+ }
}
dependencies {
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt
index 4717174e30706dffa4a82760fddb38a5d8562a0b..b6cf73e696710e2ff0bc9705b54a528d583e9fb3 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt
@@ -144,4 +144,6 @@ object Icons {
val VisibilityOff: ImageVector
get() = MaterialIcons.Filled.VisibilityOff
}
+
+ object DualTone
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt
index e7bbc1b37da7c7aca1e9329fa0caca995d064a60..65a89395c4a5e88d8df273f75d63cf3bc15ef4f9 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/text/TextBodyMedium.kt
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import app.k9mail.core.ui.compose.theme2.MainTheme
@@ -17,6 +18,7 @@ fun TextBodyMedium(
textAlign: TextAlign? = null,
overflow: TextOverflow = TextOverflow.Clip,
maxLines: Int = Int.MAX_VALUE,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
) {
Material3Text(
text = text,
@@ -26,6 +28,7 @@ fun TextBodyMedium(
overflow = overflow,
maxLines = maxLines,
style = MainTheme.typography.bodyMedium,
+ onTextLayout = onTextLayout,
)
}
@@ -37,6 +40,7 @@ fun TextBodyMedium(
textAlign: TextAlign? = null,
overflow: TextOverflow = TextOverflow.Clip,
maxLines: Int = Int.MAX_VALUE,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
) {
Material3Text(
text = text,
@@ -46,5 +50,6 @@ fun TextBodyMedium(
overflow = overflow,
maxLines = maxLines,
style = MainTheme.typography.bodyMedium,
+ onTextLayout = onTextLayout,
)
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/BannerInlineNotificationCard.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/BannerInlineNotificationCard.kt
index 7995435ce55181a1808d40690645a64f48399f10..921bb431022da894017adc1b589aed4114fe433f 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/BannerInlineNotificationCard.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/BannerInlineNotificationCard.kt
@@ -14,6 +14,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextOverflow
import app.k9mail.core.ui.compose.designsystem.atom.card.CardColors
import app.k9mail.core.ui.compose.designsystem.atom.card.CardOutlined
@@ -54,6 +55,7 @@ internal fun BannerInlineNotificationCard(
border: BorderStroke = BannerNotificationCardDefaults.errorCardBorder(),
shape: Shape = BannerNotificationCardDefaults.bannerInlineShape,
behaviour: BannerInlineNotificationCardBehaviour = BannerNotificationCardDefaults.bannerInlineBehaviour,
+ onSupportingTextOverflow: (hasVisualOverflow: Boolean) -> Unit = {},
) {
val maxLines = when (behaviour) {
BannerInlineNotificationCardBehaviour.Clipped -> 2
@@ -74,6 +76,11 @@ internal fun BannerInlineNotificationCard(
supportingText = supportingText,
behaviour = behaviour,
maxLines = maxLines,
+ onTextOverflow = { hasVisualOverflow ->
+ if (behaviour == BannerInlineNotificationCardBehaviour.Clipped) {
+ onSupportingTextOverflow(hasVisualOverflow)
+ }
+ },
)
},
actions = actions,
@@ -186,11 +193,12 @@ private fun BannerInlineNotificationTitle(
}
@Composable
-fun BannerInlineNotificationSupportingText(
+private fun BannerInlineNotificationSupportingText(
supportingText: CharSequence,
behaviour: BannerInlineNotificationCardBehaviour,
maxLines: Int,
modifier: Modifier = Modifier,
+ onTextOverflow: (hasVisualOverflow: Boolean) -> Unit = {},
) {
val clippedSupportingText = remember(supportingText, behaviour) {
when (behaviour) {
@@ -200,12 +208,16 @@ fun BannerInlineNotificationSupportingText(
else -> supportingText
}
}
+ val onTextLayout = remember<(TextLayoutResult) -> Unit>(onTextOverflow) {
+ { textLayoutResult -> onTextOverflow(textLayoutResult.hasVisualOverflow) }
+ }
when (clippedSupportingText) {
is String -> TextBodyMedium(
text = clippedSupportingText,
maxLines = maxLines,
overflow = TextOverflow.Ellipsis,
modifier = modifier,
+ onTextLayout = onTextLayout,
)
is AnnotatedString -> TextBodyMedium(
@@ -213,6 +225,7 @@ fun BannerInlineNotificationSupportingText(
maxLines = maxLines,
overflow = TextOverflow.Ellipsis,
modifier = modifier,
+ onTextLayout = onTextLayout,
)
else -> TextBodyMedium(
@@ -220,6 +233,7 @@ fun BannerInlineNotificationSupportingText(
maxLines = maxLines,
overflow = TextOverflow.Ellipsis,
modifier = modifier,
+ onTextLayout = onTextLayout,
)
}
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/ErrorBannerInlineNotificationCard.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/ErrorBannerInlineNotificationCard.kt
index 25bb7da9a3f092c18bedfbaae4dfb298990a2b4a..ec31b09809990b7d4c895c1cfc8d5a6ba1e373a4 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/ErrorBannerInlineNotificationCard.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/ErrorBannerInlineNotificationCard.kt
@@ -25,6 +25,7 @@ fun ErrorBannerInlineNotificationCard(
actions: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier,
behaviour: BannerInlineNotificationCardBehaviour = BannerNotificationCardDefaults.bannerInlineBehaviour,
+ onSupportingTextOverflow: (hasVisualOverflow: Boolean) -> Unit = {},
) {
BannerInlineNotificationCard(
icon = { Icon(imageVector = Icons.Outlined.Report) },
@@ -35,5 +36,6 @@ fun ErrorBannerInlineNotificationCard(
behaviour = behaviour,
colors = BannerNotificationCardDefaults.errorCardColors(),
border = BannerNotificationCardDefaults.errorCardBorder(),
+ onSupportingTextOverflow = onSupportingTextOverflow,
)
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/InfoBannerInlineNotificationCard.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/InfoBannerInlineNotificationCard.kt
index e835d7d18c8c5d235e94bd2d2f42fca8164174da..4a536bfd5632ce7efcd984adaa2e6eba6ad6bd20 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/InfoBannerInlineNotificationCard.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/InfoBannerInlineNotificationCard.kt
@@ -25,6 +25,7 @@ fun InfoBannerInlineNotificationCard(
actions: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier,
behaviour: BannerInlineNotificationCardBehaviour = BannerNotificationCardDefaults.bannerInlineBehaviour,
+ onSupportingTextOverflow: (hasVisualOverflow: Boolean) -> Unit = {},
) {
BannerInlineNotificationCard(
icon = { Icon(imageVector = Icons.Outlined.Info) },
@@ -35,5 +36,6 @@ fun InfoBannerInlineNotificationCard(
behaviour = behaviour,
colors = BannerNotificationCardDefaults.infoCardColors(),
border = BannerNotificationCardDefaults.infoCardBorder(),
+ onSupportingTextOverflow = onSupportingTextOverflow,
)
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/SuccessBannerInlineNotificationCard.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/SuccessBannerInlineNotificationCard.kt
index 102c7c253d76cd28d7616d38ba774a2d1a2e841c..597bb92955126990a918d9e2e2bb235358a6fcc5 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/SuccessBannerInlineNotificationCard.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/SuccessBannerInlineNotificationCard.kt
@@ -25,6 +25,7 @@ fun SuccessBannerInlineNotificationCard(
actions: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier,
behaviour: BannerInlineNotificationCardBehaviour = BannerNotificationCardDefaults.bannerInlineBehaviour,
+ onSupportingTextOverflow: (hasVisualOverflow: Boolean) -> Unit = {},
) {
BannerInlineNotificationCard(
icon = { Icon(imageVector = Icons.Outlined.CheckCircle) },
@@ -35,5 +36,6 @@ fun SuccessBannerInlineNotificationCard(
behaviour = behaviour,
colors = BannerNotificationCardDefaults.successCardColors(),
border = BannerNotificationCardDefaults.successCardBorder(),
+ onSupportingTextOverflow = onSupportingTextOverflow,
)
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/WarningBannerInlineNotificationCard.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/WarningBannerInlineNotificationCard.kt
index 24cded285b5b958ad3fa978a2076506c2c56d1a6..253456f35adc91928d316a2db286e4a207e7d151 100644
--- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/WarningBannerInlineNotificationCard.kt
+++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/banner/inline/WarningBannerInlineNotificationCard.kt
@@ -26,6 +26,7 @@ fun WarningBannerInlineNotificationCard(
actions: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier,
behaviour: BannerInlineNotificationCardBehaviour = BannerNotificationCardDefaults.bannerInlineBehaviour,
+ onSupportingTextOverflow: (hasVisualOverflow: Boolean) -> Unit = {},
) {
BannerInlineNotificationCard(
icon = { Icon(imageVector = Icons.Outlined.Warning) },
@@ -36,5 +37,6 @@ fun WarningBannerInlineNotificationCard(
behaviour = behaviour,
colors = BannerNotificationCardDefaults.warningCardColors(),
border = BannerNotificationCardDefaults.warningCardBorder(),
+ onSupportingTextOverflow = onSupportingTextOverflow,
)
}
diff --git a/core/ui/compose/designsystem/src/main/kotlin/net/thunderbird/core/ui/compose/designsystem/atom/icon/dualtone/Warning.kt b/core/ui/compose/designsystem/src/main/kotlin/net/thunderbird/core/ui/compose/designsystem/atom/icon/dualtone/Warning.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e5f23ba12702e511c3b55f6e85053e941e58cd8a
--- /dev/null
+++ b/core/ui/compose/designsystem/src/main/kotlin/net/thunderbird/core/ui/compose/designsystem/atom/icon/dualtone/Warning.kt
@@ -0,0 +1,220 @@
+package net.thunderbird.core.ui.compose.designsystem.atom.icon.dualtone
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
+import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
+
+@Suppress("MagicNumber", "UnusedReceiverParameter")
+val Icons.DualTone.Warning: ImageVector
+ get() {
+ val current = _warningDualTone
+ if (current != null) return current
+
+ return ImageVector.Builder(
+ name = "app.k9mail.core.ui.compose.theme2.MainTheme.WarningDualTone",
+ defaultWidth = 24.0.dp,
+ defaultHeight = 24.0.dp,
+ viewportWidth = 24.0f,
+ viewportHeight = 24.0f,
+ ).apply {
+ path(
+ fill = SolidColor(Color(0xFF4C4D58)),
+ fillAlpha = 0.2f,
+ strokeAlpha = 0.2f,
+ ) {
+ moveTo(x = 20.2f, y = 20.25f)
+ horizontalLineTo(x = 3.8f)
+ arcToRelative(
+ a = 1.5f,
+ b = 1.5f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = -1.35f,
+ dy1 = -2.24f,
+ )
+ lineToRelative(dx = 8.2f, dy = -14.24f)
+ arcToRelative(
+ a = 1.57f,
+ b = 1.57f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = 2.7f,
+ dy1 = 0.0f,
+ )
+ lineToRelative(dx = 8.2f, dy = 14.24f)
+ arcToRelative(
+ a = 1.5f,
+ b = 1.5f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = -1.35f,
+ dy1 = 2.24f,
+ )
+ }
+ path(
+ fill = SolidColor(Color(0xFF4C4D58)),
+ ) {
+ moveTo(x = 22.2f, y = 17.63f)
+ lineTo(x = 14.0f, y = 3.4f)
+ arcToRelative(
+ a = 2.32f,
+ b = 2.32f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = false,
+ dx1 = -4.0f,
+ dy1 = 0.0f,
+ )
+ lineTo(x = 1.8f, y = 17.63f)
+ arcToRelative(
+ a = 2.2f,
+ b = 2.2f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = false,
+ dx1 = 0.0f,
+ dy1 = 2.23f,
+ )
+ arcToRelative(
+ a = 2.3f,
+ b = 2.3f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = false,
+ dx1 = 2.0f,
+ dy1 = 1.14f,
+ )
+ horizontalLineToRelative(dx = 16.4f)
+ arcToRelative(
+ a = 2.3f,
+ b = 2.3f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = false,
+ dx1 = 2.3f,
+ dy1 = -2.25f,
+ )
+ arcToRelative(
+ a = 2.0f,
+ b = 2.0f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = false,
+ dx1 = -0.3f,
+ dy1 = -1.12f,
+ )
+ moveToRelative(dx = -1.3f, dy = 1.48f)
+ arcToRelative(
+ a = 0.8f,
+ b = 0.8f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = -0.7f,
+ dy1 = 0.39f,
+ )
+ horizontalLineTo(x = 3.8f)
+ arcToRelative(
+ a = 0.8f,
+ b = 0.8f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = -0.7f,
+ dy1 = -0.4f,
+ )
+ arcToRelative(
+ a = 0.7f,
+ b = 0.7f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = 0.0f,
+ dy1 = -0.72f,
+ )
+ lineToRelative(dx = 8.2f, dy = -14.24f)
+ arcToRelative(
+ a = 0.82f,
+ b = 0.82f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = 1.4f,
+ dy1 = 0.0f,
+ )
+ lineToRelative(dx = 8.2f, dy = 14.24f)
+ arcToRelative(
+ a = 0.7f,
+ b = 0.7f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = 0.0f,
+ dy1 = 0.73f,
+ )
+ moveToRelative(dx = -9.65f, dy = -5.61f)
+ verticalLineTo(y = 9.75f)
+ arcToRelative(
+ a = 0.75f,
+ b = 0.75f,
+ theta = 0.0f,
+ isMoreThanHalf = true,
+ isPositiveArc = true,
+ dx1 = 1.5f,
+ dy1 = 0.0f,
+ )
+ verticalLineToRelative(dy = 3.75f)
+ arcToRelative(
+ a = 0.75f,
+ b = 0.75f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = -1.5f,
+ dy1 = 0.0f,
+ )
+ moveToRelative(dx = 1.88f, dy = 3.38f)
+ arcToRelative(
+ a = 1.13f,
+ b = 1.13f,
+ theta = 0.0f,
+ isMoreThanHalf = true,
+ isPositiveArc = true,
+ dx1 = -2.26f,
+ dy1 = 0.0f,
+ )
+ arcToRelative(
+ a = 1.13f,
+ b = 1.13f,
+ theta = 0.0f,
+ isMoreThanHalf = false,
+ isPositiveArc = true,
+ dx1 = 2.26f,
+ dy1 = 0.0f,
+ )
+ }
+ }.build().also { _warningDualTone = it }
+ }
+
+@Suppress("ObjectPropertyName")
+private var _warningDualTone: ImageVector? = null
+
+@Preview(showBackground = true)
+@Composable
+private fun Preview() {
+ Column {
+ Image(imageVector = Icons.DualTone.Warning, contentDescription = null)
+ Icon(imageVector = Icons.DualTone.Warning, contentDescription = null, tint = Color.Red)
+ }
+}
diff --git a/core/ui/compose/designsystem/src/main/res/values-ht/strings.xml b/core/ui/compose/designsystem/src/main/res/values-ht/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/core/ui/compose/designsystem/src/main/res/values-ht/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/core/ui/compose/designsystem/src/main/res/values-kn/strings.xml b/core/ui/compose/designsystem/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55344e51920f3dcbb968d32ee2281e029d1571bf
--- /dev/null
+++ b/core/ui/compose/designsystem/src/main/res/values-kn/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/core/ui/compose/designsystem/src/main/res/values-mnw/strings.xml b/core/ui/compose/designsystem/src/main/res/values-mnw/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e8e4ac678331dcfc9cccf3bef4c08e838fbdca6c
--- /dev/null
+++ b/core/ui/compose/designsystem/src/main/res/values-mnw/strings.xml
@@ -0,0 +1,4 @@
+
+
+ ပၞုက်လိက်ဓလုက်
+
diff --git a/core/ui/compose/designsystem/src/main/res/values-sl/strings.xml b/core/ui/compose/designsystem/src/main/res/values-sl/strings.xml
index 23cc55b65869dcb9c9443c06b7e6f2d2812775ff..479d417967f94c72a57bb9112990c212699583a4 100644
--- a/core/ui/compose/designsystem/src/main/res/values-sl/strings.xml
+++ b/core/ui/compose/designsystem/src/main/res/values-sl/strings.xml
@@ -2,7 +2,7 @@
Skrij gesloPokaži geslo
- Elektronski naslov
+ E-poštni naslovGesloPoskusi znova
-
\ No newline at end of file
+
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorViewPreview.kt
deleted file mode 100644
index ee50ff30f313841298fcb7eed3d686598efd12fe..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemColorViewPreview.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
-import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
-
-@Composable
-@Preview(showBackground = true)
-internal fun PreferenceItemColorViewPreview() {
- PreviewWithThemes {
- PreferenceItemColorView(
- preference = FakePreferenceData.colorPreference,
- onClick = {},
- )
- }
-}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSingleChoiceCompactViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSingleChoiceCompactViewPreview.kt
deleted file mode 100644
index c0a5b2a0239e1eae1f1d57fa3fbf696caca9a3e4..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSingleChoiceCompactViewPreview.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
-import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
-
-@Composable
-@Preview(showBackground = true)
-internal fun PreferenceItemSingleChoiceCompactViewPreview() {
- PreviewWithThemes {
- PreferenceItemSingleChoiceCompactView(
- preference = FakePreferenceData.singleChoiceCompactPreference,
- onClick = {},
- )
- }
-}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSingleChoiceViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSingleChoiceViewPreview.kt
deleted file mode 100644
index c3868d2bf6e5f577c28ac604f90ddce162d84c56..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSingleChoiceViewPreview.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
-import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
-
-@Composable
-@Preview(showBackground = true)
-internal fun PreferenceItemSingleChoiceViewPreview() {
- PreviewWithThemes {
- PreferenceItemSingleChoiceView(
- preference = FakePreferenceData.singleChoicePreference.copy(description = { null }),
- onPreferenceChange = {},
- )
- }
-}
-
-@Composable
-@Preview(showBackground = true)
-internal fun PreferenceItemSingleChoiceViewWithDescriptionPreview() {
- PreviewWithThemes {
- PreferenceItemSingleChoiceView(
- preference = FakePreferenceData.singleChoicePreference,
- onPreferenceChange = {},
- )
- }
-}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSwitchViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSwitchViewPreview.kt
deleted file mode 100644
index a540d38c34dbc6aa054a8813999275908735e4fd..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemSwitchViewPreview.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
-import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
-
-@Composable
-@Preview(showBackground = true)
-internal fun Preview_Switch_On_Enabled() {
- PreviewWithThemes {
- PreferenceItemSwitchView(
- preference = FakePreferenceData.switchPreference,
- onPreferenceChange = {},
- )
- }
-}
-
-@Composable
-@Preview(showBackground = true)
-internal fun Preview_Switch_Off_Enabled() {
- PreviewWithThemes {
- PreferenceItemSwitchView(
- preference = FakePreferenceData.switchPreference.copy(value = false),
- onPreferenceChange = {},
- )
- }
-}
-
-@Composable
-@Preview(showBackground = true)
-internal fun Preview_Switch_On_Disabled() {
- PreviewWithThemes {
- PreferenceItemSwitchView(
- preference = FakePreferenceData.switchPreference.copy(enabled = false),
- onPreferenceChange = {},
- )
- }
-}
-
-@Composable
-@Preview(showBackground = true)
-internal fun Preview_Switch_Off_Disabled() {
- PreviewWithThemes {
- PreferenceItemSwitchView(
- preference = FakePreferenceData.switchPreference.copy(value = false, enabled = false),
- onPreferenceChange = {},
- )
- }
-}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextViewPreview.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextViewPreview.kt
deleted file mode 100644
index d76833eab562840f89cb402470eddf17dd45ac54..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextViewPreview.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
-import net.thunderbird.core.ui.compose.preference.ui.fake.FakePreferenceData
-
-@Composable
-@Preview(showBackground = true)
-internal fun PreferenceItemTextViewPreview() {
- PreviewWithThemes {
- PreferenceItemTextView(
- preference = FakePreferenceData.textPreference,
- onClick = {},
- )
- }
-}
diff --git a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt b/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt
deleted file mode 100644
index bd6f17234d761e4236ddaed88317d4a71e3fa4d9..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/debug/kotlin/net/thunderbird/core/ui/compose/preference/ui/fake/FakePreferenceData.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.fake
-
-import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
-import kotlinx.collections.immutable.persistentListOf
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting.SingleChoice.Choice
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting.SingleChoiceCompact.CompactChoice
-
-internal object FakePreferenceData {
-
- val textPreference = PreferenceSetting.Text(
- id = "text",
- icon = { Icons.Outlined.Delete },
- title = { "Title" },
- description = { "Description" },
- value = "Value",
- )
-
- val colorPreference = PreferenceSetting.Color(
- id = "color",
- icon = { Icons.Outlined.Delete },
- title = { "Title" },
- description = { "Description" },
- value = 0xFFFF0000.toInt(),
- colors = persistentListOf(
- 0xFFFF0000.toInt(),
- 0xFF00FF00.toInt(),
- 0xFF0000FF.toInt(),
- ),
- )
-
- private val choices = persistentListOf(
- Choice("1") { "Choice 1" },
- Choice("2") { "Choice 2" },
- Choice("3") { "Choice 3" },
- )
-
- val singleChoicePreference = PreferenceSetting.SingleChoice(
- id = "single_choice",
- title = { "Title" },
- description = { "Description" },
- value = choices[1],
- options = choices,
- )
-
- private val compactChoices = persistentListOf(
- CompactChoice("1") { "Compact Choice 1" },
- CompactChoice("2") { "Compact Choice 2" },
- CompactChoice("3") { "Compact Choice 3" },
- CompactChoice("1") { "Compact Choice 4" },
- CompactChoice("2") { "Compact Choice 5" },
- CompactChoice("3") { "Compact Choice 6" },
- )
-
- val singleChoiceCompactPreference = PreferenceSetting.SingleChoiceCompact(
- id = "single_choice_compact",
- title = { "Title" },
- icon = { Icons.Outlined.Info },
- description = { "Description" },
- value = compactChoices[1],
- options = compactChoices,
- )
-
- val switchPreference = PreferenceSetting.Switch(
- id = "switch",
- title = { "Title" },
- description = { "Description" },
- enabled = true,
- value = true,
- )
-
- val preferences = persistentListOf(
- textPreference,
- colorPreference,
- switchPreference,
- singleChoicePreference,
- )
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt
deleted file mode 100644
index f4e4d117439638962018aa2f18b74238d312113f..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/api/Preference.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.api
-
-import android.os.Parcelable
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting.SingleChoice.Choice
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting.SingleChoiceCompact.CompactChoice
-
-/**
- * A preference that can be displayed in a preference screen.
- */
-sealed interface Preference : Parcelable {
- val id: String
-}
-
-/**
- * A preference that holds a value of type [T].
- */
-sealed interface PreferenceSetting : Preference {
- val value: T
- val requiresEditView: Boolean
-
- @Parcelize
- data class Text(
- override val id: String,
- val title: () -> String,
- val description: () -> String? = { null },
- val icon: () -> ImageVector? = { null },
- override val value: String,
- ) : PreferenceSetting {
- @IgnoredOnParcel
- override val requiresEditView: Boolean = true
- }
-
- @Parcelize
- data class Color(
- override val id: String,
- val title: () -> String,
- val description: () -> String? = { null },
- val icon: () -> ImageVector? = { null },
- override val value: Int,
- val colors: ImmutableList,
- ) : PreferenceSetting {
- @IgnoredOnParcel
- override val requiresEditView: Boolean = true
- }
-
- @Parcelize
- data class SingleChoice(
- override val id: String,
- val title: () -> String,
- val description: () -> String? = { null },
- override val value: Choice,
- val options: ImmutableList,
- ) : PreferenceSetting {
- @IgnoredOnParcel
- override val requiresEditView: Boolean = false
-
- @Parcelize
- data class Choice(
- val id: String,
- val title: () -> String,
- ) : Parcelable
- }
-
- @Parcelize
- data class SingleChoiceCompact(
- override val id: String,
- val title: () -> String,
- val description: () -> String? = { null },
- val icon: () -> ImageVector? = { null },
- override val value: CompactChoice,
- val options: ImmutableList,
- ) : PreferenceSetting {
- @IgnoredOnParcel
- override val requiresEditView: Boolean = true
-
- @Parcelize
- data class CompactChoice(
- val id: String,
- val title: () -> String,
- ) : Parcelable
- }
-
- @Parcelize
- data class Switch(
- override val id: String,
- val title: () -> String,
- val description: () -> String? = { null },
- val enabled: Boolean,
- override val value: Boolean,
- ) : PreferenceSetting {
- @IgnoredOnParcel
- override val requiresEditView: Boolean = false
- }
-}
-
-/**
- * A preference that does not hold a value. It is used to display a section, a divider or custom UI.
- */
-sealed interface PreferenceDisplay : Preference {
-
- @Parcelize
- data class Custom(
- override val id: String,
- val customUi: @Composable (Modifier) -> Unit,
- ) : PreferenceDisplay
-
- @Parcelize
- data class SectionHeader(
- override val id: String,
- val title: () -> String,
- val color: () -> Color = { Color.Unspecified },
- ) : PreferenceDisplay
-
- @Parcelize
- data class SectionDivider(
- override val id: String,
- ) : PreferenceDisplay
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/PreferenceView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/PreferenceView.kt
deleted file mode 100644
index d8a4f4d1f3d32d88d4b6bbe0a2ced265532631b6..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/PreferenceView.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import kotlinx.collections.immutable.ImmutableList
-import net.thunderbird.core.ui.compose.preference.api.Preference
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-
-/**
- * A view that displays a list of preferences.
- *
- * @param title The title of the view.
- * @param subtitle The subtitle of the view (optional).
- * @param preferences The list of preferences to display.
- * @param onPreferenceChange The callback to be invoked when a preference is changed.
- * @param onBack The callback to be invoked when the back button is clicked.
- * @param modifier The modifier to be applied to the view.
- */
-@Composable
-fun PreferenceView(
- title: String,
- preferences: ImmutableList,
- onPreferenceChange: (PreferenceSetting<*>) -> Unit,
- onBack: () -> Unit,
- modifier: Modifier = Modifier,
- subtitle: String? = null,
-) {
- PreferenceViewWithDialog(
- title = title,
- subtitle = subtitle,
- preferences = preferences,
- onPreferenceChange = onPreferenceChange,
- onBack = onBack,
- modifier = modifier,
- )
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt
deleted file mode 100644
index 7f7a305b24afbb3f8b9cbf5c78456fc69431d0f9..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/dialog/PreferenceDialog.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.dialog
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import net.thunderbird.core.ui.compose.preference.api.Preference
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-
-@Composable
-internal fun PreferenceDialog(
- preference: Preference,
- onConfirmClick: (PreferenceSetting<*>) -> Unit,
- onDismissClick: () -> Unit,
- onDismissRequest: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- require(preference is PreferenceSetting<*>) {
- "Unsupported preference type: ${preference::class.java.simpleName}"
- }
-
- when (preference) {
- is PreferenceSetting.Text -> {
- PreferenceDialogTextView(
- preference = preference,
- onConfirmClick = onConfirmClick,
- onDismissClick = onDismissClick,
- onDismissRequest = onDismissRequest,
- modifier = modifier,
- )
- }
-
- is PreferenceSetting.Color -> {
- PreferenceDialogColorView(
- preference = preference,
- onConfirmClick = onConfirmClick,
- onDismissClick = onDismissClick,
- onDismissRequest = onDismissRequest,
- modifier = modifier,
- )
- }
-
- is PreferenceSetting.SingleChoiceCompact -> {
- PreferenceDialogSingleChoiceCompactView(
- preference = preference,
- onConfirmClick = onConfirmClick,
- onDismissClick = onDismissClick,
- onDismissRequest = onDismissRequest,
- modifier = modifier,
- )
- }
-
- // No dialog needed
- is PreferenceSetting.SingleChoice, is PreferenceSetting.Switch -> Unit
- }
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt
deleted file mode 100644
index 24349e5e734b5b419769c12fe04e3e7bc6dba1f2..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItem.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import net.thunderbird.core.ui.compose.preference.api.Preference
-import net.thunderbird.core.ui.compose.preference.api.PreferenceDisplay
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-
-@Composable
-internal fun PreferenceItem(
- preference: Preference,
- onClick: () -> Unit,
- onPreferenceChange: (PreferenceSetting<*>) -> Unit,
- modifier: Modifier = Modifier,
-) {
- when (preference) {
- // PreferenceSetting
- is PreferenceSetting.Text -> {
- PreferenceItemTextView(
- preference = preference,
- onClick = onClick,
- modifier = modifier,
- )
- }
-
- is PreferenceSetting.Color -> {
- PreferenceItemColorView(
- preference = preference,
- onClick = onClick,
- modifier = modifier,
- )
- }
-
- is PreferenceSetting.SingleChoice -> {
- PreferenceItemSingleChoiceView(
- preference = preference,
- onPreferenceChange = onPreferenceChange,
- modifier = modifier,
- )
- }
-
- is PreferenceSetting.Switch -> {
- PreferenceItemSwitchView(
- preference = preference,
- onPreferenceChange = onPreferenceChange,
- modifier = modifier,
- )
- }
-
- is PreferenceSetting.SingleChoiceCompact -> PreferenceItemSingleChoiceCompactView(
- preference = preference,
- onClick = onClick,
- modifier = modifier,
- )
-
- // PreferenceDisplay
- is PreferenceDisplay.Custom -> {
- PreferenceItemCustomView(
- preference = preference,
- modifier = modifier,
- )
- }
-
- is PreferenceDisplay.SectionHeader -> {
- PreferenceItemSectionHeaderView(
- preference = preference,
- modifier = modifier,
- )
- }
-
- is PreferenceDisplay.SectionDivider -> {
- PreferenceItemSectionDividerView(
- modifier = modifier,
- )
- }
- }
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemCustomView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemCustomView.kt
deleted file mode 100644
index bc1227cd7b2c9eb45807878dc2535d875838ba3c..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemCustomView.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import net.thunderbird.core.ui.compose.preference.api.PreferenceDisplay
-
-@Composable
-internal fun PreferenceItemCustomView(
- preference: PreferenceDisplay.Custom,
- modifier: Modifier = Modifier,
-) {
- preference.customUi(modifier)
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt
deleted file mode 100644
index a07672cfb848e3c95565e82091ad8f2e162dc1b3..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceItemTextView.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
-import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-
-@Composable
-internal fun PreferenceItemTextView(
- preference: PreferenceSetting.Text,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- PreferenceItemLayout(
- onClick = onClick,
- icon = preference.icon(),
- modifier = modifier,
- ) {
- TextTitleMedium(text = preference.title())
- TextBodyMedium(text = preference.value)
- }
-}
diff --git a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceList.kt b/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceList.kt
deleted file mode 100644
index b27d95d4e5c7e242c0c21a9101f11820278c8061..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/kotlin/net/thunderbird/core/ui/compose/preference/ui/components/list/PreferenceList.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package net.thunderbird.core.ui.compose.preference.ui.components.list
-
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import kotlinx.collections.immutable.ImmutableList
-import net.thunderbird.core.ui.compose.preference.api.Preference
-import net.thunderbird.core.ui.compose.preference.api.PreferenceSetting
-
-@Composable
-internal fun PreferenceList(
- preferences: ImmutableList,
- onItemClick: (index: Int, item: Preference) -> Unit,
- onPreferenceChange: (PreferenceSetting<*>) -> Unit,
- modifier: Modifier = Modifier,
-) {
- LazyColumn(
- modifier = modifier,
- ) {
- itemsIndexed(preferences) { index, item ->
- PreferenceItem(
- preference = item,
- onClick = {
- onItemClick(index, item)
- },
- onPreferenceChange = onPreferenceChange,
- modifier = Modifier.fillMaxWidth(),
- )
- }
- }
-}
diff --git a/core/ui/compose/preference/src/main/res/values/strings.xml b/core/ui/compose/preference/src/main/res/values/strings.xml
deleted file mode 100644
index 3d01935e6516164e8a8546a7dc29afbdce7b5976..0000000000000000000000000000000000000000
--- a/core/ui/compose/preference/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- Accept
- Cancel
-
diff --git a/core/ui/legacy/theme2/k9mail/src/main/res/drawable/ic_logo_k9_white.xml b/core/ui/legacy/theme2/k9mail/src/main/res/drawable/ic_logo_k9_white.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a1f0d0b8a8bd0608f284249c73a5d3be461f6fbb
--- /dev/null
+++ b/core/ui/legacy/theme2/k9mail/src/main/res/drawable/ic_logo_k9_white.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/ui/legacy/theme2/thunderbird/src/main/res/drawable/ic_logo_thunderbird_white.xml b/core/ui/legacy/theme2/thunderbird/src/main/res/drawable/ic_logo_thunderbird_white.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a2efb445b2ad24b38767c754753341f13e176704
--- /dev/null
+++ b/core/ui/legacy/theme2/thunderbird/src/main/res/drawable/ic_logo_thunderbird_white.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/core/ui/setting/api/build.gradle.kts b/core/ui/setting/api/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..13b3b9a15087aba2e93a7753ba4aaccb83139636
--- /dev/null
+++ b/core/ui/setting/api/build.gradle.kts
@@ -0,0 +1,7 @@
+plugins {
+ id(ThunderbirdPlugins.Library.kmpCompose)
+}
+
+android {
+ namespace = "net.thunderbird.core.ui.setting"
+}
diff --git a/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/Setting.kt b/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/Setting.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5cb7ef6d613c7e718c249513ea883a82c8e19514
--- /dev/null
+++ b/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/Setting.kt
@@ -0,0 +1,8 @@
+package net.thunderbird.core.ui.setting
+
+/**
+ * A setting that can be displayed in a setting screen.
+ */
+sealed interface Setting {
+ val id: String
+}
diff --git a/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/SettingDecoration.kt b/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/SettingDecoration.kt
new file mode 100644
index 0000000000000000000000000000000000000000..200a133fb764c2f3053437905d1adbddcb07cd88
--- /dev/null
+++ b/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/SettingDecoration.kt
@@ -0,0 +1,37 @@
+package net.thunderbird.core.ui.setting
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+
+/**
+ * A setting decoration could be used for enhancing the UI of a settings screen. It does not hold any value.
+ *
+ * Examples include section headers, dividers, or custom UI components.
+ */
+sealed interface SettingDecoration : Setting {
+
+ /**
+ * A setting that displays custom UI.
+ */
+ data class Custom(
+ override val id: String,
+ val customUi: @Composable (Modifier) -> Unit,
+ ) : SettingDecoration
+
+ /**
+ * A setting that displays a section header.
+ */
+ data class SectionHeader(
+ override val id: String,
+ val title: () -> String,
+ val color: () -> Color = { Color.Unspecified },
+ ) : SettingDecoration
+
+ /**
+ * A setting that displays a divider.
+ */
+ data class SectionDivider(
+ override val id: String,
+ ) : SettingDecoration
+}
diff --git a/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/SettingValue.kt b/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/SettingValue.kt
new file mode 100644
index 0000000000000000000000000000000000000000..91165b4710a7adcedbe6311b8edf2b136e71b744
--- /dev/null
+++ b/core/ui/setting/api/src/commonMain/kotlin/net/thunderbird/core/ui/setting/SettingValue.kt
@@ -0,0 +1,139 @@
+package net.thunderbird.core.ui.setting
+
+import androidx.compose.ui.graphics.vector.ImageVector
+import kotlinx.collections.immutable.ImmutableList
+import net.thunderbird.core.ui.setting.SettingValue.CompactSelectSingleOption.CompactOption
+import net.thunderbird.core.ui.setting.SettingValue.SelectSingleOption.Option
+
+/**
+ * A setting that holds a value of type [T].
+ */
+sealed interface SettingValue : Setting {
+ val value: T
+ val requiresEditView: Boolean
+
+ /**
+ * A setting that holds a string value.
+ *
+ * This requires an edit view to modify the value.
+ *
+ * @param id The unique identifier for the setting.
+ * @param title A lambda that returns the title of the setting.
+ * @param description A lambda that returns the description of the setting. Default is null.
+ * @param icon A lambda that returns the icon of the setting as an [ImageVector]. Default is null.
+ * @param value The current value of the setting.
+ */
+ data class Text(
+ override val id: String,
+ val title: () -> String,
+ val description: () -> String? = { null },
+ val icon: () -> ImageVector? = { null },
+ override val value: String,
+ ) : SettingValue {
+ override val requiresEditView: Boolean = true
+ }
+
+ /**
+ * A setting that holds a color value.
+ *
+ * This requires an edit view to select a color from the provided list of colors.
+ *
+ * @param id The unique identifier for the setting.
+ * @param title A lambda that returns the title of the setting.
+ * @param description A lambda that returns the description of the setting. Default is null.
+ * @param icon A lambda that returns the icon of the setting as an [ImageVector]. Default is null.
+ * @param value The current color value of the setting, represented as an integer.
+ */
+ data class Color(
+ override val id: String,
+ val title: () -> String,
+ val description: () -> String? = { null },
+ val icon: () -> ImageVector? = { null },
+ override val value: Int,
+ val colors: ImmutableList,
+ ) : SettingValue {
+ override val requiresEditView: Boolean = true
+ }
+
+ /**
+ * A setting that allows the user to select a single option from a list of options.
+ *
+ * The options are displayed in a compact manner, suitable for scenarios where space is limited.
+ * The number of options must be between 2 and 4.
+ *
+ * This requires no edit view to modify the value. The selection can be made directly from the setting item.
+ *
+ * @param id The unique identifier for the setting.
+ * @param title A lambda that returns the title of the setting.
+ * @param description A lambda that returns the description of the setting. Default is null.
+ * @param value The currently selected option.
+ * @param options The list of available options to choose from.
+ */
+ data class CompactSelectSingleOption(
+ override val id: String,
+ val title: () -> String,
+ val description: () -> String? = { null },
+ override val value: CompactOption,
+ val options: ImmutableList>,
+ ) : SettingValue> {
+ override val requiresEditView: Boolean = false
+
+ init {
+ require(options.size >= 2) { "There must be at least two options." }
+ require(options.size <= 4) { "There can be at most four options." }
+ }
+
+ data class CompactOption(
+ val id: String,
+ val title: () -> String,
+ val value: T,
+ )
+ }
+
+ /**
+ * A setting that allows the user to select a single option from a list of options.
+ *
+ * Requires an edit view to select the option from the provided list of options.
+ *
+ * @param id The unique identifier for the setting.
+ * @param title A lambda that returns the title of the setting.
+ * @param description A lambda that returns the description of the setting. Default is null.
+ * @param icon A lambda that returns the icon of the setting as an [ImageVector]. Default is null.
+ * @param value The currently selected option.
+ * @param options The list of available options to choose from.
+ */
+ data class SelectSingleOption(
+ override val id: String,
+ val title: () -> String,
+ val description: () -> String? = { null },
+ val icon: () -> ImageVector? = { null },
+ override val value: Option,
+ val options: ImmutableList