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 com.fsck.k9.backend.BackendFactory
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.core.scope.Scope import org.koin.core.qualifier.named
fun Scope.developmentBackends() = emptyMap<String, BackendFactory>()
fun Module.developmentModuleAdditions() { fun Module.developmentModuleAdditions() {
// No-op single<Map<String, BackendFactory>>(named("developmentBackends")) {
emptyMap()
}
} }

View file

@ -1,11 +1,5 @@
plugins { plugins {
id(ThunderbirdPlugins.App.android) id(ThunderbirdPlugins.Library.android)
alias(libs.plugins.dependency.guard)
}
val testCoverageEnabled: Boolean by extra
if (testCoverageEnabled) {
apply(plugin = "jacoco")
} }
dependencies { dependencies {
@ -16,7 +10,6 @@ dependencies {
implementation(projects.app.cryptoOpenpgp) implementation(projects.app.cryptoOpenpgp)
implementation(projects.backend.imap) implementation(projects.backend.imap)
implementation(projects.backend.pop3) implementation(projects.backend.pop3)
debugImplementation(projects.backend.demo)
implementation(projects.core.featureflags) implementation(projects.core.featureflags)
implementation(projects.feature.launcher) implementation(projects.feature.launcher)
@ -45,127 +38,14 @@ dependencies {
} }
android { android {
namespace = "com.fsck.k9" namespace = "com.fsck.k9.common"
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 { 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 { debug {
applicationIdSuffix = ".debug" manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
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"
} }
} release {
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",
)
} }
} }
} }
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,33 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto"> android:installLocation="auto">
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"
android:required="false"/> android:required="false" />
<supports-screens <supports-screens
android:anyDensity="true" android:anyDensity="true"
android:largeScreens="true" android:largeScreens="true"
android:normalScreens="true" android:normalScreens="true"
android:smallScreens="true"/> android:smallScreens="true" />
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<application <application
android:name="com.fsck.k9.App"
android:allowTaskReparenting="false" android:allowTaskReparenting="false"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
@ -43,96 +41,96 @@
<meta-data <meta-data
android:name="android.app.default_searchable" android:name="android.app.default_searchable"
android:value="com.fsck.k9.activity.Search"/> android:value="com.fsck.k9.activity.Search" />
<!-- TODO: Remove once minSdkVersion has been changed to 24+ --> <!-- TODO: Remove once minSdkVersion has been changed to 24+ -->
<meta-data <meta-data
android:name="com.lge.support.SPLIT_WINDOW" android:name="com.lge.support.SPLIT_WINDOW"
android:value="true"/> android:value="true" />
<uses-library <uses-library
android:name="com.sec.android.app.multiwindow" android:name="com.sec.android.app.multiwindow"
android:required="false"/> android:required="false" />
<meta-data <meta-data
android:name="com.sec.android.support.multiwindow" android:name="com.sec.android.support.multiwindow"
android:value="true"/> android:value="true" />
<meta-data <meta-data
android:name="com.samsung.android.sdk.multiwindow.penwindow.enable" android:name="com.samsung.android.sdk.multiwindow.penwindow.enable"
android:value="true"/> android:value="true" />
<meta-data android:name="android.webkit.WebView.MetricsOptOut" <meta-data
android:name="android.webkit.WebView.MetricsOptOut"
android:value="true" /> android:value="true" />
<activity <activity
android:name=".ui.settings.account.OpenPgpAppSelectDialog" android:name="com.fsck.k9.ui.settings.account.OpenPgpAppSelectDialog"
android:configChanges="locale" android:configChanges="locale"
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight" android:theme="@style/Theme.K9.Dialog.Translucent.DayNight" />
/>
<activity <activity
android:name=".activity.setup.AccountSetupComposition" android:name="com.fsck.k9.activity.setup.AccountSetupComposition"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/account_settings_composition_title"/> android:label="@string/account_settings_composition_title" />
<activity <activity
android:name=".activity.ChooseAccount" android:name="com.fsck.k9.activity.ChooseAccount"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/choose_account_title" android:label="@string/choose_account_title"
android:noHistory="true" /> android:noHistory="true" />
<activity <activity
android:name=".ui.choosefolder.ChooseFolderActivity" android:name="com.fsck.k9.ui.choosefolder.ChooseFolderActivity"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/choose_folder_title" android:label="@string/choose_folder_title"
android:noHistory="true" /> android:noHistory="true" />
<activity <activity
android:name=".activity.ChooseIdentity" android:name="com.fsck.k9.activity.ChooseIdentity"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/choose_identity_title" /> android:label="@string/choose_identity_title" />
<activity <activity
android:name=".activity.ManageIdentities" android:name="com.fsck.k9.activity.ManageIdentities"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/manage_identities_title"/> android:label="@string/manage_identities_title" />
<activity <activity
android:name=".activity.EditIdentity" android:name="com.fsck.k9.activity.EditIdentity"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/edit_identity_title"/> android:label="@string/edit_identity_title" />
<activity <activity
android:name=".ui.notification.DeleteConfirmationActivity" android:name="com.fsck.k9.ui.notification.DeleteConfirmationActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity="" android:taskAffinity=""
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight"/> android:theme="@style/Theme.K9.Dialog.Translucent.DayNight" />
<activity <activity
android:name=".ui.endtoend.AutocryptKeyTransferActivity" android:name="com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/ac_transfer_title" android:label="@string/ac_transfer_title" />
/>
<activity <activity
android:name=".activity.MessageList" android:name="com.fsck.k9.activity.MessageList"
android:launchMode="singleTop" android:launchMode="singleTop"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_EMAIL"/> <category android:name="android.intent.category.APP_EMAIL" />
<!-- TODO: Remove once minSdkVersion has been changed to 24+ --> <!-- TODO: Remove once minSdkVersion has been changed to 24+ -->
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/> <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
<category android:name="android.intent.category.PENWINDOW_LAUNCHER"/> <category android:name="android.intent.category.PENWINDOW_LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW" />
<data <data
android:host="messages" android:host="messages"
android:scheme="k9mail"/> android:scheme="k9mail" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
@ -140,107 +138,107 @@
This component is disabled by default. It will be enabled programmatically after an account has been set up. This component is disabled by default. It will be enabled programmatically after an account has been set up.
--> -->
<activity <activity
android:name=".activity.MessageCompose" android:name="com.fsck.k9.activity.MessageCompose"
android:configChanges="locale" android:configChanges="locale"
android:enabled="false" android:enabled="false"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SENDTO"/> <action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto"/> <data android:scheme="mailto" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*"/> <data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE"/> <action android:name="android.intent.action.SEND_MULTIPLE" />
<data android:mimeType="*/*"/> <data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW" />
<data android:scheme="mailto"/> <data android:scheme="mailto" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="org.autocrypt.PEER_ACTION"/> <action android:name="org.autocrypt.PEER_ACTION" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Search Activity - searchable --> <!-- Search Activity - searchable -->
<activity <activity
android:name=".activity.Search" android:name="com.fsck.k9.activity.Search"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/search_action" android:label="@string/search_action"
android:uiOptions="splitActionBarWhenNarrow" android:uiOptions="splitActionBarWhenNarrow"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEARCH"/> <action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.app.searchable" android:name="android.app.searchable"
android:resource="@xml/searchable"/> android:resource="@xml/searchable" />
</activity> </activity>
<!-- <!--
This component is disabled by default. It will be enabled programmatically after an account has been set up. This component is disabled by default. It will be enabled programmatically after an account has been set up.
--> -->
<activity <activity
android:name=".activity.LauncherShortcuts" android:name="com.fsck.k9.activity.LauncherShortcuts"
android:configChanges="locale" android:configChanges="locale"
android:label="@string/shortcuts_title" android:label="@string/shortcuts_title"
android:enabled="false" android:enabled="false"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT"/> <action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".widget.unread.UnreadWidgetConfigurationActivity" android:name="com.fsck.k9.widget.unread.UnreadWidgetConfigurationActivity"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".activity.UpgradeDatabases" android:name="com.fsck.k9.activity.UpgradeDatabases"
android:label="@string/upgrade_databases_title"/> android:label="@string/upgrade_databases_title" />
<activity <activity
android:name=".ui.managefolders.ManageFoldersActivity" android:name="com.fsck.k9.ui.managefolders.ManageFoldersActivity"
android:label="@string/folders_action" /> android:label="@string/folders_action" />
<activity <activity
android:name=".ui.settings.SettingsActivity" android:name="com.fsck.k9.ui.settings.SettingsActivity"
android:label="@string/prefs_title" /> android:label="@string/prefs_title" />
<activity <activity
android:name=".ui.settings.general.GeneralSettingsActivity" android:name="com.fsck.k9.ui.settings.general.GeneralSettingsActivity"
android:label="@string/general_settings_title" /> android:label="@string/general_settings_title" />
<activity <activity
android:name=".ui.settings.account.AccountSettingsActivity" android:name="com.fsck.k9.ui.settings.account.AccountSettingsActivity"
android:label="@string/account_settings_title_fmt" /> android:label="@string/account_settings_title_fmt" />
<activity <activity
android:name=".ui.messagesource.MessageSourceActivity" android:name="com.fsck.k9.ui.messagesource.MessageSourceActivity"
android:label="@string/show_headers_action" /> android:label="@string/show_headers_action" />
<activity <activity
android:name=".ui.changelog.RecentChangesActivity" android:name="com.fsck.k9.ui.changelog.RecentChangesActivity"
android:label="@string/changelog_recent_changes_title" /> android:label="@string/changelog_recent_changes_title" />
<activity <activity
android:name=".ui.push.PushInfoActivity" android:name="com.fsck.k9.ui.push.PushInfoActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="false" android:exported="false"
android:label="@string/push_info_title" android:label="@string/push_info_title"
@ -252,27 +250,27 @@
</activity> </activity>
<activity <activity
android:name=".activity.setup.OAuthFlowActivity" android:name="com.fsck.k9.activity.setup.OAuthFlowActivity"
android:label="@string/account_setup_basics_title" /> android:label="@string/account_setup_basics_title" />
<!-- This component is disabled by default (if possible). It will be enabled programmatically if necessary. --> <!-- This component is disabled by default (if possible). It will be enabled programmatically if necessary. -->
<receiver <receiver
android:name=".provider.UnreadWidgetProvider" android:name="com.fsck.k9.provider.UnreadWidgetProvider"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/unread_widget_label" android:label="@string/unread_widget_label"
android:enabled="@bool/home_screen_widgets_enabled" android:enabled="@bool/home_screen_widgets_enabled"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/unread_widget_info"/> android:resource="@xml/unread_widget_info" />
</receiver> </receiver>
<!-- This component is disabled by default (if possible). It will be enabled programmatically if necessary. --> <!-- This component is disabled by default (if possible). It will be enabled programmatically if necessary. -->
<receiver <receiver
android:name=".widget.list.MessageListWidgetProvider" android:name="com.fsck.k9.widget.list.MessageListWidgetProvider"
android:icon="@drawable/message_list_widget_preview" android:icon="@drawable/message_list_widget_preview"
android:label="@string/mail_list_widget_text" android:label="@string/mail_list_widget_text"
android:enabled="@bool/home_screen_widgets_enabled" android:enabled="@bool/home_screen_widgets_enabled"
@ -287,28 +285,27 @@
<!-- This component is disabled by default. It will be enabled programmatically if necessary. --> <!-- This component is disabled by default. It will be enabled programmatically if necessary. -->
<receiver <receiver
android:name=".controller.push.BootCompleteReceiver" android:name="com.fsck.k9.controller.push.BootCompleteReceiver"
android:exported="false" android:exported="false"
android:enabled="false"> android:enabled="false">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<service <service android:name="com.fsck.k9.notification.NotificationActionService" />
android:name=".notification.NotificationActionService"/>
<service <service
android:name=".service.DatabaseUpgradeService" android:name="com.fsck.k9.service.DatabaseUpgradeService"
android:exported="false"/> android:exported="false" />
<service <service
android:name=".controller.push.PushService" android:name="com.fsck.k9.controller.push.PushService"
android:exported="false" android:exported="false"
android:foregroundServiceType="dataSync" /> android:foregroundServiceType="dataSync" />
<provider <provider
android:name=".provider.AttachmentProvider" android:name="com.fsck.k9.provider.AttachmentProvider"
android:authorities="${applicationId}.attachmentprovider" android:authorities="${applicationId}.attachmentprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
@ -320,7 +317,7 @@
</provider> </provider>
<provider <provider
android:name=".provider.RawMessageProvider" android:name="com.fsck.k9.provider.RawMessageProvider"
android:authorities="${applicationId}.rawmessageprovider" android:authorities="${applicationId}.rawmessageprovider"
android:exported="false"> android:exported="false">
@ -331,7 +328,7 @@
</provider> </provider>
<provider <provider
android:name=".provider.DecryptedFileProvider" android:name="com.fsck.k9.provider.DecryptedFileProvider"
android:authorities="${applicationId}.decryptedfileprovider" android:authorities="${applicationId}.decryptedfileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
@ -342,7 +339,7 @@
</provider> </provider>
<provider <provider
android:name=".provider.AttachmentTempFileProvider" android:name="com.fsck.k9.provider.AttachmentTempFileProvider"
android:authorities="${applicationId}.tempfileprovider" android:authorities="${applicationId}.tempfileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
@ -363,7 +360,9 @@
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<!-- Microsoft uses a special redirect URI format for Android apps --> <!-- 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> </intent-filter>
</activity> </activity>

View file

@ -4,16 +4,12 @@ import android.app.Application
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import app.k9mail.ui.widget.list.MessageListWidgetManager 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.controller.MessagingController
import com.fsck.k9.job.WorkManagerConfigurationProvider import com.fsck.k9.job.WorkManagerConfigurationProvider
import com.fsck.k9.notification.NotificationChannelManager 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.AppLanguageManager
import com.fsck.k9.ui.base.ThemeManager import com.fsck.k9.ui.base.ThemeManager
import com.fsck.k9.ui.base.extensions.currentLocale import com.fsck.k9.ui.base.extensions.currentLocale
import com.fsck.k9.widget.list.MessageListWidgetProvider
import java.util.Locale import java.util.Locale
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -24,10 +20,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.core.module.Module
import timber.log.Timber import timber.log.Timber
import androidx.work.Configuration as WorkManagerConfiguration 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 messagingController: MessagingController by inject()
private val messagingListenerProvider: MessagingListenerProvider by inject() private val messagingListenerProvider: MessagingListenerProvider by inject()
private val themeManager: ThemeManager by inject() private val themeManager: ThemeManager by inject()
@ -44,7 +41,7 @@ class App : Application(), WorkManagerConfiguration.Provider {
super.onCreate() super.onCreate()
DI.start(this, coreModules + uiModules + appModules) DI.start(this, listOf(provideAppModule()) + coreModules + uiModules + commonAppModules)
K9.init(this) K9.init(this)
Core.init(this) Core.init(this)
@ -58,6 +55,8 @@ class App : Application(), WorkManagerConfiguration.Provider {
} }
} }
abstract fun provideAppModule(): Module
private fun initializeAppLanguage() { private fun initializeAppLanguage() {
appLanguageManager.init() appLanguageManager.init()
applyOverrideLocaleToConfiguration() applyOverrideLocaleToConfiguration()
@ -130,15 +129,4 @@ class App : Application(), WorkManagerConfiguration.Provider {
override val workManagerConfiguration: WorkManagerConfiguration override val workManagerConfiguration: WorkManagerConfiguration
get() = workManagerConfigurationProvider.getConfiguration() 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 package com.fsck.k9
import app.k9mail.core.common.oauth.OAuthConfigurationFactory
import app.k9mail.core.featureflag.FeatureFlagFactory import app.k9mail.core.featureflag.FeatureFlagFactory
import app.k9mail.core.featureflag.FeatureFlagProvider import app.k9mail.core.featureflag.FeatureFlagProvider
import app.k9mail.core.featureflag.InMemoryFeatureFlagProvider import app.k9mail.core.featureflag.InMemoryFeatureFlagProvider
import app.k9mail.ui.widget.list.messageListWidgetModule import app.k9mail.ui.widget.list.messageListWidgetModule
import com.fsck.k9.account.newAccountModule import com.fsck.k9.account.newAccountModule
import com.fsck.k9.auth.AppOAuthConfigurationFactory
import com.fsck.k9.backends.backendsModule import com.fsck.k9.backends.backendsModule
import com.fsck.k9.controller.ControllerExtension import com.fsck.k9.controller.ControllerExtension
import com.fsck.k9.crypto.EncryptionExtractor 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.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
private val mainAppModule = module { val commonAppModule = module {
single { App.appConfig }
single { single {
MessagingListenerProvider( MessagingListenerProvider(
listOf( listOf(
@ -36,7 +33,6 @@ private val mainAppModule = module {
single(named("controllerExtensions")) { emptyList<ControllerExtension>() } single(named("controllerExtensions")) { emptyList<ControllerExtension>() }
single<EncryptionExtractor> { OpenPgpEncryptionExtractor.newInstance() } single<EncryptionExtractor> { OpenPgpEncryptionExtractor.newInstance() }
single<StoragePersister> { K9StoragePersister(get()) } single<StoragePersister> { K9StoragePersister(get()) }
single<OAuthConfigurationFactory> { AppOAuthConfigurationFactory() }
single<FeatureFlagFactory> { InMemoryFeatureFlagFactory() } single<FeatureFlagFactory> { InMemoryFeatureFlagFactory() }
single<FeatureFlagProvider> { single<FeatureFlagProvider> {
InMemoryFeatureFlagProvider( InMemoryFeatureFlagProvider(
@ -45,8 +41,8 @@ private val mainAppModule = module {
} }
} }
val appModules = listOf( val commonAppModules = listOf(
mainAppModule, commonAppModule,
messageListWidgetConfigModule, messageListWidgetConfigModule,
messageListWidgetModule, messageListWidgetModule,
unreadWidgetModule, unreadWidgetModule,

View file

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

View file

@ -1,7 +1,6 @@
package com.fsck.k9.backends package com.fsck.k9.backends
import android.content.Context import android.content.Context
import com.fsck.k9.BuildConfig
import com.fsck.k9.mail.AuthenticationFailedException import com.fsck.k9.mail.AuthenticationFailedException
import com.fsck.k9.mail.oauth.AuthStateStorage import com.fsck.k9.mail.oauth.AuthStateStorage
import com.fsck.k9.mail.oauth.OAuth2TokenProvider import com.fsck.k9.mail.oauth.OAuth2TokenProvider
@ -18,6 +17,7 @@ import timber.log.Timber
class RealOAuth2TokenProvider( class RealOAuth2TokenProvider(
context: Context, context: Context,
private val authStateStorage: AuthStateStorage, private val authStateStorage: AuthStateStorage,
) : OAuth2TokenProvider { ) : OAuth2TokenProvider {
private val authService = AuthorizationService(context) private val authService = AuthorizationService(context)
private var requestFreshToken = false private var requestFreshToken = false
@ -49,11 +49,6 @@ class RealOAuth2TokenProvider(
latch.await(timeoutMillis, TimeUnit.MILLISECONDS) latch.await(timeoutMillis, TimeUnit.MILLISECONDS)
} catch (e: Exception) { } 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.") Timber.w(e, "Failed to fetch an access token. Clearing authorization state.")
authStateStorage.updateAuthorizationState(authorizationState = null) authStateStorage.updateAuthorizationState(authorizationState = null)

View file

@ -9,7 +9,7 @@ import android.content.Intent
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import com.fsck.k9.EarlyInit 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.helper.PendingIntentCompat.FLAG_MUTABLE
import com.fsck.k9.inject import com.fsck.k9.inject
import com.fsck.k9.widget.unread.UnreadWidgetConfigurationActivity 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.appwidget.AppWidgetManager
import android.os.Bundle 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.base.K9Activity
import com.fsck.k9.ui.fragmentTransaction import com.fsck.k9.ui.fragmentTransaction
import timber.log.Timber import timber.log.Timber

View file

@ -12,8 +12,8 @@ import androidx.core.os.bundleOf
import androidx.preference.CheckBoxPreference import androidx.preference.CheckBoxPreference
import androidx.preference.Preference import androidx.preference.Preference
import com.fsck.k9.Preferences import com.fsck.k9.Preferences
import com.fsck.k9.R
import com.fsck.k9.activity.ChooseAccount import com.fsck.k9.activity.ChooseAccount
import com.fsck.k9.common.R
import com.fsck.k9.search.SearchAccount import com.fsck.k9.search.SearchAccount
import com.fsck.k9.ui.choosefolder.ChooseFolderActivity import com.fsck.k9.ui.choosefolder.ChooseFolderActivity
import com.takisoft.preferencex.PreferenceFragmentCompat import com.takisoft.preferencex.PreferenceFragmentCompat
@ -98,6 +98,7 @@ class UnreadWidgetConfigurationFragment : PreferenceFragmentCompat() {
val accountUuid = data.getStringExtra(ChooseAccount.EXTRA_ACCOUNT_UUID)!! val accountUuid = data.getStringExtra(ChooseAccount.EXTRA_ACCOUNT_UUID)!!
handleChooseAccount(accountUuid) handleChooseAccount(accountUuid)
} }
REQUEST_CHOOSE_FOLDER -> { REQUEST_CHOOSE_FOLDER -> {
val folderId = data.getLongExtra(ChooseFolderActivity.RESULT_SELECTED_FOLDER_ID, -1L) val folderId = data.getLongExtra(ChooseFolderActivity.RESULT_SELECTED_FOLDER_ID, -1L)
val folderDisplayName = data.getStringExtra(ChooseFolderActivity.RESULT_FOLDER_DISPLAY_NAME)!! val folderDisplayName = data.getStringExtra(ChooseFolderActivity.RESULT_FOLDER_DISPLAY_NAME)!!
@ -163,6 +164,7 @@ class UnreadWidgetConfigurationFragment : PreferenceFragmentCompat() {
} }
true true
} }
else -> super.onOptionsItemSelected(item) 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 import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@Config(application = App::class) @Config(application = TestApp::class)
class DependencyInjectionTest : AutoCloseKoinTest() { class DependencyInjectionTest : AutoCloseKoinTest() {
private val lifecycleOwner = mock<LifecycleOwner> { private val lifecycleOwner = mock<LifecycleOwner> {
on { lifecycle } doReturn mock() 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.isEqualTo
import assertk.assertions.isNull import assertk.assertions.isNull
import com.fsck.k9.Account import com.fsck.k9.Account
import com.fsck.k9.AppRobolectricTest
import com.fsck.k9.Preferences import com.fsck.k9.Preferences
import com.fsck.k9.TestApp
import com.fsck.k9.controller.MessageCounts import com.fsck.k9.controller.MessageCounts
import com.fsck.k9.controller.MessageCountsProvider import com.fsck.k9.controller.MessageCountsProvider
import com.fsck.k9.mailstore.Folder 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.folders.FolderNameFormatter
import com.fsck.k9.ui.messagelist.DefaultFolderProvider import com.fsck.k9.ui.messagelist.DefaultFolderProvider
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith
import org.koin.test.AutoCloseKoinTest
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment 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 context: Context = RuntimeEnvironment.getApplication()
private val account = createAccount() private val account = createAccount()
private val preferences = createPreferences() private val preferences = createPreferences()

View file

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