Add app-k9mail module to host the app project

This commit is contained in:
Wolf-Martell Montwé 2024-01-10 18:14:40 +01:00
parent 4c68731371
commit b88f23a18e
No known key found for this signature in database
GPG key ID: 6D45B21512ACBF72
29 changed files with 522 additions and 298 deletions

147
app-k9mail/build.gradle.kts Normal file
View file

@ -0,0 +1,147 @@
plugins {
id(ThunderbirdPlugins.App.android)
alias(libs.plugins.dependency.guard)
}
val testCoverageEnabled: Boolean by extra
if (testCoverageEnabled) {
apply(plugin = "jacoco")
}
dependencies {
implementation(projects.app.k9mail)
implementation(projects.app.core)
implementation(projects.app.ui.legacy)
implementation(projects.app.ui.messageListWidget)
debugImplementation(projects.backend.demo)
implementation(libs.androidx.work.runtime)
}
android {
namespace = "com.fsck.k9"
defaultConfig {
applicationId = "com.fsck.k9"
testApplicationId = "com.fsck.k9.tests"
versionCode = 37014
versionName = "6.715-SNAPSHOT"
// Keep in sync with the resource string array "supported_languages"
resourceConfigurations.addAll(
listOf(
"in", "br", "ca", "cs", "cy", "da", "de", "et", "en", "en_GB", "es", "eo", "eu", "fr", "gd", "gl",
"hr", "is", "it", "lv", "lt", "hu", "nl", "nb", "pl", "pt_PT", "pt_BR", "ru", "ro", "sq", "sk", "sl",
"fi", "sv", "tr", "el", "be", "bg", "sr", "uk", "iw", "ar", "fa", "ml", "ko", "zh_CN", "zh_TW", "ja",
"fy",
),
)
buildConfigField("String", "CLIENT_ID_APP_NAME", "\"K-9 Mail\"")
}
signingConfigs {
if (project.hasProperty("k9mail.keyAlias") &&
project.hasProperty("k9mail.keyPassword") &&
project.hasProperty("k9mail.storeFile") &&
project.hasProperty("k9mail.storePassword")
) {
create("release") {
keyAlias = project.property("k9mail.keyAlias") as String
keyPassword = project.property("k9mail.keyPassword") as String
storeFile = file(project.property("k9mail.storeFile") as String)
storePassword = project.property("k9mail.storePassword") as String
}
}
}
buildTypes {
release {
signingConfig = signingConfigs.findByName("release")
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro",
)
buildConfigField(
"String",
"OAUTH_GMAIL_CLIENT_ID",
"\"262622259280-hhmh92rhklkg2k1tjil69epo0o9a12jm.apps.googleusercontent.com\"",
)
buildConfigField(
"String",
"OAUTH_YAHOO_CLIENT_ID",
"\"dj0yJmk9aHNUb3d2MW5TQnpRJmQ9WVdrOWVYbHpaRWM0YkdnbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWIz\"",
)
buildConfigField(
"String",
"OAUTH_AOL_CLIENT_ID",
"\"dj0yJmk9dUNqYXZhYWxOYkdRJmQ9WVdrOU1YQnZVRFZoY1ZrbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWIw\"",
)
buildConfigField("String", "OAUTH_MICROSOFT_CLIENT_ID", "\"e647013a-ada4-4114-b419-e43d250f99c5\"")
buildConfigField(
"String",
"OAUTH_MICROSOFT_REDIRECT_URI",
"\"msauth://com.fsck.k9/Dx8yUsuhyU3dYYba1aA16Wxu5eM%3D\"",
)
manifestPlaceholders["appAuthRedirectScheme"] = "com.fsck.k9"
}
debug {
applicationIdSuffix = ".debug"
enableUnitTestCoverage = testCoverageEnabled
enableAndroidTestCoverage = testCoverageEnabled
isMinifyEnabled = false
buildConfigField(
"String",
"OAUTH_GMAIL_CLIENT_ID",
"\"262622259280-5qb3vtj68d5dtudmaif4g9vd3cpar8r3.apps.googleusercontent.com\"",
)
buildConfigField(
"String",
"OAUTH_YAHOO_CLIENT_ID",
"\"dj0yJmk9ejRCRU1ybmZjQlVBJmQ9WVdrOVVrZEViak4xYmxZbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTZj\"",
)
buildConfigField(
"String",
"OAUTH_AOL_CLIENT_ID",
"\"dj0yJmk9cHYydkJkTUxHcXlYJmQ9WVdrOWVHZHhVVXN4VVV3bWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTdm\"",
)
buildConfigField("String", "OAUTH_MICROSOFT_CLIENT_ID", "\"e647013a-ada4-4114-b419-e43d250f99c5\"")
buildConfigField(
"String",
"OAUTH_MICROSOFT_REDIRECT_URI",
"\"msauth://com.fsck.k9.debug/VZF2DYuLYAu4TurFd6usQB2JPts%3D\"",
)
manifestPlaceholders["appAuthRedirectScheme"] = "com.fsck.k9.debug"
}
}
packaging {
jniLibs {
excludes += listOf("kotlin/**")
}
resources {
excludes += listOf(
"META-INF/*.kotlin_module",
"META-INF/*.version",
"kotlin/**",
"DebugProbesKt.bin",
)
}
}
}
dependencyGuard {
configuration("releaseRuntimeClasspath")
}

View file

@ -0,0 +1,12 @@
package app.k9mail.dev
import com.fsck.k9.backend.BackendFactory
import org.koin.core.module.Module
import org.koin.core.qualifier.named
fun Module.developmentModuleAdditions() {
single { DemoBackendFactory(backendStorageFactory = get()) }
single<Map<String, BackendFactory>>(named("developmentBackends")) {
mapOf("demo" to get<DemoBackendFactory>())
}
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto">
<application
android:name="com.fsck.k9.K9App"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" />
</manifest>

View file

@ -0,0 +1,7 @@
package com.fsck.k9
import org.koin.core.module.Module
class K9App : CommonApp() {
override fun provideAppModule(): Module = appModule
}

View file

@ -0,0 +1,29 @@
package com.fsck.k9
import app.k9mail.core.common.oauth.OAuthConfigurationFactory
import app.k9mail.dev.developmentModuleAdditions
import com.fsck.k9.activity.LauncherShortcuts
import com.fsck.k9.activity.MessageCompose
import com.fsck.k9.auth.AppOAuthConfigurationFactory
import com.fsck.k9.provider.UnreadWidgetProvider
import com.fsck.k9.widget.list.MessageListWidgetProvider
import org.koin.core.qualifier.named
import org.koin.dsl.module
val appModule = module {
single(named("ClientIdAppName")) { BuildConfig.CLIENT_ID_APP_NAME }
single(named("ClientIdAppVersion")) { BuildConfig.VERSION_NAME }
single<AppConfig> { appConfig }
single<OAuthConfigurationFactory> { AppOAuthConfigurationFactory() }
developmentModuleAdditions()
}
val appConfig = AppConfig(
componentsToDisable = listOf(
MessageCompose::class.java,
LauncherShortcuts::class.java,
UnreadWidgetProvider::class.java,
MessageListWidgetProvider::class.java,
),
)

View file

@ -0,0 +1,150 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:viewportWidth="192"
android:viewportHeight="192">
<path
android:fillColor="#607d8b"
android:fillType="evenOdd"
android:pathData="m32,116v12l25.61,38c2.07,3.59 5.94,6 10.39,6h56c4.46,0 8.32,-2.41 10.39,-6h0.01l25.6,-38v-12z"
android:strokeWidth="0.376"
android:strokeColor="#00000000"
android:strokeLineCap="butt"
android:strokeLineJoin="miter" />
<path
android:fillColor="#263238"
android:fillType="nonZero"
android:pathData="M64,16h8v28h-8z"
android:strokeWidth="5.99999952"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#263238"
android:fillType="nonZero"
android:pathData="M120,16h8v28h-8z"
android:strokeWidth="5.99999952"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#4d6570"
android:fillType="nonZero"
android:pathData="m32,127v1l25.61,38c2.07,3.59 5.94,6 10.39,6h56c4.46,0 8.32,-2.41 10.39,-6h0.01l25.6,-38v-1l-25.6,38h-0.01c-2.07,3.59 -5.94,6 -10.39,6h-56c-4.46,0 -8.32,-2.41 -10.39,-6z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#607d8b"
android:fillType="nonZero"
android:pathData="M80,14L80,22A6,6 0,0 1,74 28L50,28A6,6 0,0 1,44 22L44,14A6,6 0,0 1,50 8L74,8A6,6 0,0 1,80 14z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#607d8b"
android:fillType="nonZero"
android:pathData="M148,14L148,22A6,6 0,0 1,142 28L118,28A6,6 0,0 1,112 22L112,14A6,6 0,0 1,118 8L142,8A6,6 0,0 1,148 14z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#4d6570"
android:fillType="nonZero"
android:pathData="m44,21v1c0,3.32 2.68,6 6,6h24c3.32,0 6,-2.68 6,-6v-1c0,3.32 -2.68,6 -6,6h-24c-3.32,0 -6,-2.68 -6,-6z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#4d6570"
android:fillType="nonZero"
android:pathData="m112,21v1c0,3.32 2.68,6 6,6h24c3.32,0 6,-2.68 6,-6v-1c0,3.32 -2.68,6 -6,6h-24c-3.32,0 -6,-2.68 -6,-6z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#8097a2"
android:fillType="nonZero"
android:pathData="m50,8c-3.32,0 -6,2.68 -6,6v1c0,-3.32 2.68,-6 6,-6h24c3.32,0 6,2.68 6,6v-1c0,-3.32 -2.68,-6 -6,-6z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#8097a2"
android:fillType="nonZero"
android:pathData="m118,8c-3.32,0 -6,2.68 -6,6v1c0,-3.32 2.68,-6 6,-6h24c3.32,0 6,2.68 6,6v-1c0,-3.32 -2.68,-6 -6,-6z"
android:strokeWidth="0.34016225"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="@color/app_logo_main"
android:fillType="nonZero"
android:pathData="M172,48L172,116A12,12 0,0 1,160 128L32,128A12,12 0,0 1,20 116L20,48A12,12 0,0 1,32 36L160,36A12,12 0,0 1,172 48z"
android:strokeWidth="0.340162"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="m36,52 l60,32 60,-32"
android:strokeWidth="6"
android:strokeColor="#fbe9e7"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="@color/app_logo_highlight_light"
android:fillType="nonZero"
android:pathData="m32,36c-6.65,0 -12,5.35 -12,12v1c0,-6.65 5.35,-12 12,-12h128c6.65,0 12,5.35 12,12v-1c0,-6.65 -5.35,-12 -12,-12z"
android:strokeWidth="0.340162"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="@color/app_logo_highlight_dark"
android:fillType="nonZero"
android:pathData="m20,115v1c0,6.65 5.35,12 12,12h128c6.65,0 12,-5.35 12,-12v-1c0,6.65 -5.35,12 -12,12h-128c-6.65,0 -12,-5.35 -12,-12z"
android:strokeWidth="0.340162"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#263238"
android:fillType="nonZero"
android:pathData="M108,158L108,170A6,6 0,0 1,102 176L90,176A6,6 0,0 1,84 170L84,158A6,6 0,0 1,90 152L102,152A6,6 0,0 1,108 158z"
android:strokeWidth="0.340162"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#263238"
android:fillType="nonZero"
android:pathData="M96,172m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"
android:strokeWidth="9"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#37474f"
android:fillType="nonZero"
android:pathData="m90,152c-3.32,0 -6,2.68 -6,6v1c0,-3.32 2.68,-6 6,-6h12c3.32,0 6,2.68 6,6v-1c0,-3.32 -2.68,-6 -6,-6z"
android:strokeWidth="0.340162"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
<path
android:fillColor="#1a252a"
android:fillType="nonZero"
android:pathData="m84.02,171.43a12,12 0,0 0,-0.02 0.57,12 12,0 0,0 12,12 12,12 0,0 0,12 -12,12 12,0 0,0 -0.02,-0.41 12,12 0,0 1,-11.98 11.41,12 12,0 0,1 -11.98,-11.57z"
android:strokeWidth="9"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="miter" />
</vector>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<drawable name="ic_launcher">@drawable/ic_app_logo</drawable>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Used in AndroidManifest.xml -->
<string name="app_name">K-9 Mail</string>
</resources>

View file

@ -2,10 +2,10 @@ package app.k9mail.dev
import com.fsck.k9.backend.BackendFactory
import org.koin.core.module.Module
import org.koin.core.scope.Scope
fun Scope.developmentBackends() = emptyMap<String, BackendFactory>()
import org.koin.core.qualifier.named
fun Module.developmentModuleAdditions() {
// No-op
single<Map<String, BackendFactory>>(named("developmentBackends")) {
emptyMap()
}
}

View file

@ -1,11 +1,5 @@
plugins {
id(ThunderbirdPlugins.App.android)
alias(libs.plugins.dependency.guard)
}
val testCoverageEnabled: Boolean by extra
if (testCoverageEnabled) {
apply(plugin = "jacoco")
id(ThunderbirdPlugins.Library.android)
}
dependencies {
@ -16,7 +10,6 @@ dependencies {
implementation(projects.app.cryptoOpenpgp)
implementation(projects.backend.imap)
implementation(projects.backend.pop3)
debugImplementation(projects.backend.demo)
implementation(projects.core.featureflags)
implementation(projects.feature.launcher)
@ -45,127 +38,14 @@ dependencies {
}
android {
namespace = "com.fsck.k9"
defaultConfig {
applicationId = "com.fsck.k9"
testApplicationId = "com.fsck.k9.tests"
versionCode = 37014
versionName = "6.715-SNAPSHOT"
// Keep in sync with the resource string array "supported_languages"
resourceConfigurations.addAll(
listOf(
"in", "br", "ca", "cs", "cy", "da", "de", "et", "en", "en_GB", "es", "eo", "eu", "fr", "gd", "gl",
"hr", "is", "it", "lv", "lt", "hu", "nl", "nb", "pl", "pt_PT", "pt_BR", "ru", "ro", "sq", "sk", "sl",
"fi", "sv", "tr", "el", "be", "bg", "sr", "uk", "iw", "ar", "fa", "ml", "ko", "zh_CN", "zh_TW", "ja",
"fy",
),
)
buildConfigField("String", "CLIENT_ID_APP_NAME", "\"K-9 Mail\"")
}
signingConfigs {
if (project.hasProperty("k9mail.keyAlias") &&
project.hasProperty("k9mail.keyPassword") &&
project.hasProperty("k9mail.storeFile") &&
project.hasProperty("k9mail.storePassword")
) {
create("release") {
keyAlias = project.property("k9mail.keyAlias") as String
keyPassword = project.property("k9mail.keyPassword") as String
storeFile = file(project.property("k9mail.storeFile") as String)
storePassword = project.property("k9mail.storePassword") as String
}
}
}
namespace = "com.fsck.k9.common"
buildTypes {
release {
signingConfig = signingConfigs.findByName("release")
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro",
)
buildConfigField(
"String",
"OAUTH_GMAIL_CLIENT_ID",
"\"262622259280-hhmh92rhklkg2k1tjil69epo0o9a12jm.apps.googleusercontent.com\"",
)
buildConfigField(
"String",
"OAUTH_YAHOO_CLIENT_ID",
"\"dj0yJmk9aHNUb3d2MW5TQnpRJmQ9WVdrOWVYbHpaRWM0YkdnbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWIz\"",
)
buildConfigField(
"String",
"OAUTH_AOL_CLIENT_ID",
"\"dj0yJmk9dUNqYXZhYWxOYkdRJmQ9WVdrOU1YQnZVRFZoY1ZrbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PWIw\"",
)
buildConfigField("String", "OAUTH_MICROSOFT_CLIENT_ID", "\"e647013a-ada4-4114-b419-e43d250f99c5\"")
buildConfigField(
"String",
"OAUTH_MICROSOFT_REDIRECT_URI",
"\"msauth://com.fsck.k9/Dx8yUsuhyU3dYYba1aA16Wxu5eM%3D\"",
)
manifestPlaceholders["appAuthRedirectScheme"] = "com.fsck.k9"
}
debug {
applicationIdSuffix = ".debug"
enableUnitTestCoverage = testCoverageEnabled
enableAndroidTestCoverage = testCoverageEnabled
isMinifyEnabled = false
buildConfigField(
"String",
"OAUTH_GMAIL_CLIENT_ID",
"\"262622259280-5qb3vtj68d5dtudmaif4g9vd3cpar8r3.apps.googleusercontent.com\"",
)
buildConfigField(
"String",
"OAUTH_YAHOO_CLIENT_ID",
"\"dj0yJmk9ejRCRU1ybmZjQlVBJmQ9WVdrOVVrZEViak4xYmxZbWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTZj\"",
)
buildConfigField(
"String",
"OAUTH_AOL_CLIENT_ID",
"\"dj0yJmk9cHYydkJkTUxHcXlYJmQ9WVdrOWVHZHhVVXN4VVV3bWNHbzlNQT09JnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTdm\"",
)
buildConfigField("String", "OAUTH_MICROSOFT_CLIENT_ID", "\"e647013a-ada4-4114-b419-e43d250f99c5\"")
buildConfigField(
"String",
"OAUTH_MICROSOFT_REDIRECT_URI",
"\"msauth://com.fsck.k9.debug/VZF2DYuLYAu4TurFd6usQB2JPts%3D\"",
)
manifestPlaceholders["appAuthRedirectScheme"] = "com.fsck.k9.debug"
manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
}
}
packaging {
jniLibs {
excludes += listOf("kotlin/**")
}
resources {
excludes += listOf(
"META-INF/*.kotlin_module",
"META-INF/*.version",
"kotlin/**",
"DebugProbesKt.bin",
)
release {
manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
}
}
}
dependencyGuard {
configuration("releaseRuntimeClasspath")
}

View file

@ -1,12 +0,0 @@
package app.k9mail.dev
import org.koin.core.module.Module
import org.koin.core.scope.Scope
fun Scope.developmentBackends() = mapOf(
"demo" to get<DemoBackendFactory>(),
)
fun Module.developmentModuleAdditions() {
single { DemoBackendFactory(backendStorageFactory = get()) }
}

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
@ -27,7 +26,6 @@
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<application
android:name="com.fsck.k9.App"
android:allowTaskReparenting="false"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
@ -52,68 +50,68 @@
<uses-library
android:name="com.sec.android.app.multiwindow"
android:required="false" />
<meta-data
android:name="com.sec.android.support.multiwindow"
android:value="true" />
<meta-data
android:name="com.samsung.android.sdk.multiwindow.penwindow.enable"
android:value="true" />
<meta-data android:name="android.webkit.WebView.MetricsOptOut"
<meta-data
android:name="android.webkit.WebView.MetricsOptOut"
android:value="true" />
<activity
android:name=".ui.settings.account.OpenPgpAppSelectDialog"
android:name="com.fsck.k9.ui.settings.account.OpenPgpAppSelectDialog"
android:configChanges="locale"
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight"
/>
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight" />
<activity
android:name=".activity.setup.AccountSetupComposition"
android:name="com.fsck.k9.activity.setup.AccountSetupComposition"
android:configChanges="locale"
android:label="@string/account_settings_composition_title" />
<activity
android:name=".activity.ChooseAccount"
android:name="com.fsck.k9.activity.ChooseAccount"
android:configChanges="locale"
android:label="@string/choose_account_title"
android:noHistory="true" />
<activity
android:name=".ui.choosefolder.ChooseFolderActivity"
android:name="com.fsck.k9.ui.choosefolder.ChooseFolderActivity"
android:configChanges="locale"
android:label="@string/choose_folder_title"
android:noHistory="true" />
<activity
android:name=".activity.ChooseIdentity"
android:name="com.fsck.k9.activity.ChooseIdentity"
android:configChanges="locale"
android:label="@string/choose_identity_title" />
<activity
android:name=".activity.ManageIdentities"
android:name="com.fsck.k9.activity.ManageIdentities"
android:configChanges="locale"
android:label="@string/manage_identities_title" />
<activity
android:name=".activity.EditIdentity"
android:name="com.fsck.k9.activity.EditIdentity"
android:configChanges="locale"
android:label="@string/edit_identity_title" />
<activity
android:name=".ui.notification.DeleteConfirmationActivity"
android:name="com.fsck.k9.ui.notification.DeleteConfirmationActivity"
android:excludeFromRecents="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight" />
<activity
android:name=".ui.endtoend.AutocryptKeyTransferActivity"
android:name="com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity"
android:configChanges="locale"
android:label="@string/ac_transfer_title"
/>
android:label="@string/ac_transfer_title" />
<activity
android:name=".activity.MessageList"
android:name="com.fsck.k9.activity.MessageList"
android:launchMode="singleTop"
android:exported="true">
<intent-filter>
@ -140,7 +138,7 @@
This component is disabled by default. It will be enabled programmatically after an account has been set up.
-->
<activity
android:name=".activity.MessageCompose"
android:name="com.fsck.k9.activity.MessageCompose"
android:configChanges="locale"
android:enabled="false"
android:exported="true">
@ -174,7 +172,7 @@
<!-- Search Activity - searchable -->
<activity
android:name=".activity.Search"
android:name="com.fsck.k9.activity.Search"
android:configChanges="locale"
android:label="@string/search_action"
android:uiOptions="splitActionBarWhenNarrow"
@ -192,7 +190,7 @@
This component is disabled by default. It will be enabled programmatically after an account has been set up.
-->
<activity
android:name=".activity.LauncherShortcuts"
android:name="com.fsck.k9.activity.LauncherShortcuts"
android:configChanges="locale"
android:label="@string/shortcuts_title"
android:enabled="false"
@ -204,7 +202,7 @@
</activity>
<activity
android:name=".widget.unread.UnreadWidgetConfigurationActivity"
android:name="com.fsck.k9.widget.unread.UnreadWidgetConfigurationActivity"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
@ -212,35 +210,35 @@
</activity>
<activity
android:name=".activity.UpgradeDatabases"
android:name="com.fsck.k9.activity.UpgradeDatabases"
android:label="@string/upgrade_databases_title" />
<activity
android:name=".ui.managefolders.ManageFoldersActivity"
android:name="com.fsck.k9.ui.managefolders.ManageFoldersActivity"
android:label="@string/folders_action" />
<activity
android:name=".ui.settings.SettingsActivity"
android:name="com.fsck.k9.ui.settings.SettingsActivity"
android:label="@string/prefs_title" />
<activity
android:name=".ui.settings.general.GeneralSettingsActivity"
android:name="com.fsck.k9.ui.settings.general.GeneralSettingsActivity"
android:label="@string/general_settings_title" />
<activity
android:name=".ui.settings.account.AccountSettingsActivity"
android:name="com.fsck.k9.ui.settings.account.AccountSettingsActivity"
android:label="@string/account_settings_title_fmt" />
<activity
android:name=".ui.messagesource.MessageSourceActivity"
android:name="com.fsck.k9.ui.messagesource.MessageSourceActivity"
android:label="@string/show_headers_action" />
<activity
android:name=".ui.changelog.RecentChangesActivity"
android:name="com.fsck.k9.ui.changelog.RecentChangesActivity"
android:label="@string/changelog_recent_changes_title" />
<activity
android:name=".ui.push.PushInfoActivity"
android:name="com.fsck.k9.ui.push.PushInfoActivity"
android:excludeFromRecents="true"
android:exported="false"
android:label="@string/push_info_title"
@ -252,12 +250,12 @@
</activity>
<activity
android:name=".activity.setup.OAuthFlowActivity"
android:name="com.fsck.k9.activity.setup.OAuthFlowActivity"
android:label="@string/account_setup_basics_title" />
<!-- This component is disabled by default (if possible). It will be enabled programmatically if necessary. -->
<receiver
android:name=".provider.UnreadWidgetProvider"
android:name="com.fsck.k9.provider.UnreadWidgetProvider"
android:icon="@drawable/ic_launcher"
android:label="@string/unread_widget_label"
android:enabled="@bool/home_screen_widgets_enabled"
@ -272,7 +270,7 @@
<!-- This component is disabled by default (if possible). It will be enabled programmatically if necessary. -->
<receiver
android:name=".widget.list.MessageListWidgetProvider"
android:name="com.fsck.k9.widget.list.MessageListWidgetProvider"
android:icon="@drawable/message_list_widget_preview"
android:label="@string/mail_list_widget_text"
android:enabled="@bool/home_screen_widgets_enabled"
@ -287,7 +285,7 @@
<!-- This component is disabled by default. It will be enabled programmatically if necessary. -->
<receiver
android:name=".controller.push.BootCompleteReceiver"
android:name="com.fsck.k9.controller.push.BootCompleteReceiver"
android:exported="false"
android:enabled="false">
<intent-filter>
@ -295,20 +293,19 @@
</intent-filter>
</receiver>
<service
android:name=".notification.NotificationActionService"/>
<service android:name="com.fsck.k9.notification.NotificationActionService" />
<service
android:name=".service.DatabaseUpgradeService"
android:name="com.fsck.k9.service.DatabaseUpgradeService"
android:exported="false" />
<service
android:name=".controller.push.PushService"
android:name="com.fsck.k9.controller.push.PushService"
android:exported="false"
android:foregroundServiceType="dataSync" />
<provider
android:name=".provider.AttachmentProvider"
android:name="com.fsck.k9.provider.AttachmentProvider"
android:authorities="${applicationId}.attachmentprovider"
android:exported="false"
android:grantUriPermissions="true">
@ -320,7 +317,7 @@
</provider>
<provider
android:name=".provider.RawMessageProvider"
android:name="com.fsck.k9.provider.RawMessageProvider"
android:authorities="${applicationId}.rawmessageprovider"
android:exported="false">
@ -331,7 +328,7 @@
</provider>
<provider
android:name=".provider.DecryptedFileProvider"
android:name="com.fsck.k9.provider.DecryptedFileProvider"
android:authorities="${applicationId}.decryptedfileprovider"
android:exported="false"
android:grantUriPermissions="true">
@ -342,7 +339,7 @@
</provider>
<provider
android:name=".provider.AttachmentTempFileProvider"
android:name="com.fsck.k9.provider.AttachmentTempFileProvider"
android:authorities="${applicationId}.tempfileprovider"
android:exported="false"
android:grantUriPermissions="true">
@ -363,7 +360,9 @@
<category android:name="android.intent.category.BROWSABLE" />
<!-- Microsoft uses a special redirect URI format for Android apps -->
<data android:scheme="msauth" android:host="${applicationId}"/>
<data
android:scheme="msauth"
android:host="${applicationId}" />
</intent-filter>
</activity>

View file

@ -4,16 +4,12 @@ import android.app.Application
import android.content.res.Configuration
import android.content.res.Resources
import app.k9mail.ui.widget.list.MessageListWidgetManager
import com.fsck.k9.activity.LauncherShortcuts
import com.fsck.k9.activity.MessageCompose
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.job.WorkManagerConfigurationProvider
import com.fsck.k9.notification.NotificationChannelManager
import com.fsck.k9.provider.UnreadWidgetProvider
import com.fsck.k9.ui.base.AppLanguageManager
import com.fsck.k9.ui.base.ThemeManager
import com.fsck.k9.ui.base.extensions.currentLocale
import com.fsck.k9.widget.list.MessageListWidgetProvider
import java.util.Locale
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -24,10 +20,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus
import org.koin.android.ext.android.inject
import org.koin.core.module.Module
import timber.log.Timber
import androidx.work.Configuration as WorkManagerConfiguration
class App : Application(), WorkManagerConfiguration.Provider {
abstract class CommonApp : Application(), WorkManagerConfiguration.Provider {
private val messagingController: MessagingController by inject()
private val messagingListenerProvider: MessagingListenerProvider by inject()
private val themeManager: ThemeManager by inject()
@ -44,7 +41,7 @@ class App : Application(), WorkManagerConfiguration.Provider {
super.onCreate()
DI.start(this, coreModules + uiModules + appModules)
DI.start(this, listOf(provideAppModule()) + coreModules + uiModules + commonAppModules)
K9.init(this)
Core.init(this)
@ -58,6 +55,8 @@ class App : Application(), WorkManagerConfiguration.Provider {
}
}
abstract fun provideAppModule(): Module
private fun initializeAppLanguage() {
appLanguageManager.init()
applyOverrideLocaleToConfiguration()
@ -130,15 +129,4 @@ class App : Application(), WorkManagerConfiguration.Provider {
override val workManagerConfiguration: WorkManagerConfiguration
get() = workManagerConfigurationProvider.getConfiguration()
companion object {
val appConfig = AppConfig(
componentsToDisable = listOf(
MessageCompose::class.java,
LauncherShortcuts::class.java,
UnreadWidgetProvider::class.java,
MessageListWidgetProvider::class.java,
),
)
}
}

View file

@ -1,12 +1,10 @@
package com.fsck.k9
import app.k9mail.core.common.oauth.OAuthConfigurationFactory
import app.k9mail.core.featureflag.FeatureFlagFactory
import app.k9mail.core.featureflag.FeatureFlagProvider
import app.k9mail.core.featureflag.InMemoryFeatureFlagProvider
import app.k9mail.ui.widget.list.messageListWidgetModule
import com.fsck.k9.account.newAccountModule
import com.fsck.k9.auth.AppOAuthConfigurationFactory
import com.fsck.k9.backends.backendsModule
import com.fsck.k9.controller.ControllerExtension
import com.fsck.k9.crypto.EncryptionExtractor
@ -24,8 +22,7 @@ import com.fsck.k9.widget.unread.unreadWidgetModule
import org.koin.core.qualifier.named
import org.koin.dsl.module
private val mainAppModule = module {
single { App.appConfig }
val commonAppModule = module {
single {
MessagingListenerProvider(
listOf(
@ -36,7 +33,6 @@ private val mainAppModule = module {
single(named("controllerExtensions")) { emptyList<ControllerExtension>() }
single<EncryptionExtractor> { OpenPgpEncryptionExtractor.newInstance() }
single<StoragePersister> { K9StoragePersister(get()) }
single<OAuthConfigurationFactory> { AppOAuthConfigurationFactory() }
single<FeatureFlagFactory> { InMemoryFeatureFlagFactory() }
single<FeatureFlagProvider> {
InMemoryFeatureFlagProvider(
@ -45,8 +41,8 @@ private val mainAppModule = module {
}
}
val appModules = listOf(
mainAppModule,
val commonAppModules = listOf(
commonAppModule,
messageListWidgetConfigModule,
messageListWidgetModule,
unreadWidgetModule,

View file

@ -1,8 +1,6 @@
package com.fsck.k9.backends
import app.k9mail.dev.developmentBackends
import app.k9mail.dev.developmentModuleAdditions
import com.fsck.k9.BuildConfig
import com.fsck.k9.backend.BackendFactory
import com.fsck.k9.backend.BackendManager
import com.fsck.k9.backend.imap.BackendIdleRefreshManager
import com.fsck.k9.backend.imap.SystemAlarmManager
@ -13,11 +11,12 @@ import org.koin.dsl.module
val backendsModule = module {
single {
val developmentBackends = get<Map<String, BackendFactory>>(named("developmentBackends"))
BackendManager(
mapOf(
"imap" to get<ImapBackendFactory>(),
"pop3" to get<Pop3BackendFactory>(),
) + developmentBackends(),
) + developmentBackends,
)
}
single {
@ -35,9 +34,5 @@ val backendsModule = module {
single<SystemAlarmManager> { AndroidAlarmManager(context = get(), alarmManager = get()) }
single<IdleRefreshManager> { BackendIdleRefreshManager(alarmManager = get()) }
single { Pop3BackendFactory(get(), get()) }
single(named("ClientIdAppName")) { BuildConfig.CLIENT_ID_APP_NAME }
single(named("ClientIdAppVersion")) { BuildConfig.VERSION_NAME }
single<OAuth2TokenProviderFactory> { RealOAuth2TokenProviderFactory(context = get()) }
developmentModuleAdditions()
}

View file

@ -1,7 +1,6 @@
package com.fsck.k9.backends
import android.content.Context
import com.fsck.k9.BuildConfig
import com.fsck.k9.mail.AuthenticationFailedException
import com.fsck.k9.mail.oauth.AuthStateStorage
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
@ -18,6 +17,7 @@ import timber.log.Timber
class RealOAuth2TokenProvider(
context: Context,
private val authStateStorage: AuthStateStorage,
) : OAuth2TokenProvider {
private val authService = AuthorizationService(context)
private var requestFreshToken = false
@ -49,11 +49,6 @@ class RealOAuth2TokenProvider(
latch.await(timeoutMillis, TimeUnit.MILLISECONDS)
} catch (e: Exception) {
// OAuth errors are communicated via the callback. If we end up here, it's probably a programming error.
if (BuildConfig.DEBUG) {
throw AssertionError("Wrong usage of AuthState.performActionWithFreshTokens()?", e)
}
Timber.w(e, "Failed to fetch an access token. Clearing authorization state.")
authStateStorage.updateAuthorizationState(authorizationState = null)

View file

@ -9,7 +9,7 @@ import android.content.Intent
import android.view.View
import android.widget.RemoteViews
import com.fsck.k9.EarlyInit
import com.fsck.k9.R
import com.fsck.k9.common.R
import com.fsck.k9.helper.PendingIntentCompat.FLAG_MUTABLE
import com.fsck.k9.inject
import com.fsck.k9.widget.unread.UnreadWidgetConfigurationActivity

View file

@ -2,7 +2,7 @@ package com.fsck.k9.widget.unread
import android.appwidget.AppWidgetManager
import android.os.Bundle
import com.fsck.k9.R
import com.fsck.k9.common.R
import com.fsck.k9.ui.base.K9Activity
import com.fsck.k9.ui.fragmentTransaction
import timber.log.Timber

View file

@ -12,8 +12,8 @@ import androidx.core.os.bundleOf
import androidx.preference.CheckBoxPreference
import androidx.preference.Preference
import com.fsck.k9.Preferences
import com.fsck.k9.R
import com.fsck.k9.activity.ChooseAccount
import com.fsck.k9.common.R
import com.fsck.k9.search.SearchAccount
import com.fsck.k9.ui.choosefolder.ChooseFolderActivity
import com.takisoft.preferencex.PreferenceFragmentCompat
@ -98,6 +98,7 @@ class UnreadWidgetConfigurationFragment : PreferenceFragmentCompat() {
val accountUuid = data.getStringExtra(ChooseAccount.EXTRA_ACCOUNT_UUID)!!
handleChooseAccount(accountUuid)
}
REQUEST_CHOOSE_FOLDER -> {
val folderId = data.getLongExtra(ChooseFolderActivity.RESULT_SELECTED_FOLDER_ID, -1L)
val folderDisplayName = data.getStringExtra(ChooseFolderActivity.RESULT_FOLDER_DISPLAY_NAME)!!
@ -163,6 +164,7 @@ class UnreadWidgetConfigurationFragment : PreferenceFragmentCompat() {
}
true
}
else -> super.onOptionsItemSelected(item)
}
}

View file

@ -1,14 +0,0 @@
package com.fsck.k9
import android.app.Application
import org.junit.runner.RunWith
import org.koin.test.AutoCloseKoinTest
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
/**
* A Robolectric test that creates an instance of our [Application] class [App].
*/
@RunWith(RobolectricTestRunner::class)
@Config(application = App::class)
abstract class AppRobolectricTest : AutoCloseKoinTest()

View file

@ -32,7 +32,7 @@ import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(application = App::class)
@Config(application = TestApp::class)
class DependencyInjectionTest : AutoCloseKoinTest() {
private val lifecycleOwner = mock<LifecycleOwner> {
on { lifecycle } doReturn mock()

View file

@ -0,0 +1,25 @@
package com.fsck.k9
import app.k9mail.core.common.oauth.OAuthConfigurationFactory
import com.fsck.k9.backend.BackendFactory
import org.koin.core.module.Module
import org.koin.core.qualifier.named
import org.koin.dsl.module
class TestApp : CommonApp() {
override fun provideAppModule(): Module = module {
single(named("ClientIdAppName")) { "ClientIdAppName" }
single(named("ClientIdAppVersion")) { "ClientIdAppVersion" }
single {
AppConfig(
componentsToDisable = emptyList(),
)
}
single<Map<String, BackendFactory>>(named("developmentBackends")) {
emptyMap()
}
single<OAuthConfigurationFactory> {
OAuthConfigurationFactory { emptyMap() }
}
}
}

View file

@ -5,8 +5,8 @@ import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isNull
import com.fsck.k9.Account
import com.fsck.k9.AppRobolectricTest
import com.fsck.k9.Preferences
import com.fsck.k9.TestApp
import com.fsck.k9.controller.MessageCounts
import com.fsck.k9.controller.MessageCountsProvider
import com.fsck.k9.mailstore.Folder
@ -16,11 +16,17 @@ import com.fsck.k9.search.SearchAccount
import com.fsck.k9.ui.folders.FolderNameFormatter
import com.fsck.k9.ui.messagelist.DefaultFolderProvider
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.test.AutoCloseKoinTest
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
class UnreadWidgetDataProviderTest : AppRobolectricTest() {
@RunWith(RobolectricTestRunner::class)
@Config(application = TestApp::class)
class UnreadWidgetDataProviderTest : AutoCloseKoinTest() {
private val context: Context = RuntimeEnvironment.getApplication()
private val account = createAccount()
private val preferences = createPreferences()

View file

@ -21,6 +21,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "k-9"
include(
":app-k9mail",
":app-feature-preview",
":app-ui-catalog",
)