Remove JMAP app
The JMAP backend is still part of the repository. Only currently unused.
This commit is contained in:
parent
fda4e8243a
commit
262f97812f
35 changed files with 0 additions and 1950 deletions
|
@ -1,135 +0,0 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'org.jetbrains.kotlin.android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
if (rootProject.testCoverage) {
|
||||
apply plugin: 'jacoco'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":app:ui:legacy")
|
||||
implementation project(":app:core")
|
||||
implementation project(":app:storage")
|
||||
implementation project(":app:crypto-openpgp")
|
||||
implementation project(":backend:imap")
|
||||
implementation project(":backend:pop3")
|
||||
implementation project(":backend:webdav")
|
||||
implementation project(":backend:jmap")
|
||||
|
||||
implementation "androidx.appcompat:appcompat:${versions.androidxAppCompat}"
|
||||
implementation "com.jakewharton.timber:timber:${versions.timber}"
|
||||
implementation "androidx.constraintlayout:constraintlayout:${versions.androidxConstraintLayout}"
|
||||
implementation "com.google.android.material:material:${versions.materialComponents}"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:${versions.androidxNavigation}"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:${versions.androidxNavigation}"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${versions.androidxLifecycle}"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:${versions.androidxLifecycle}"
|
||||
|
||||
implementation "com.github.bumptech.glide:glide:${versions.glide}"
|
||||
kapt "com.github.bumptech.glide:compiler:${versions.glide}"
|
||||
|
||||
// Required for DependencyInjectionTest to be able to resolve OpenPgpApiManager
|
||||
testImplementation project(':plugins:openpgp-api-lib:openpgp-api')
|
||||
|
||||
testImplementation "org.robolectric:robolectric:${versions.robolectric}"
|
||||
testImplementation "junit:junit:${versions.junit}"
|
||||
testImplementation "com.google.truth:truth:${versions.truth}"
|
||||
testImplementation "org.mockito:mockito-core:${versions.mockito}"
|
||||
testImplementation "org.mockito.kotlin:mockito-kotlin:${versions.mockitoKotlin}"
|
||||
testImplementation "io.insert-koin:koin-test-junit4:${versions.koin}"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion buildConfig.compileSdk
|
||||
buildToolsVersion buildConfig.buildTools
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.fsck.k9.jmap"
|
||||
testApplicationId "com.fsck.k9.jmap.tests"
|
||||
|
||||
versionCode 1
|
||||
versionName 'JMAP DEV'
|
||||
|
||||
minSdkVersion buildConfig.minSdk
|
||||
targetSdkVersion buildConfig.targetSdk
|
||||
|
||||
generatedDensities = ['mdpi', 'hdpi', 'xhdpi']
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
if (project.hasProperty('storeFile')) {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
|
||||
buildConfigField "boolean", "DEVELOPER_MODE", "false"
|
||||
}
|
||||
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
testCoverageEnabled rootProject.testCoverage
|
||||
|
||||
minifyEnabled false
|
||||
|
||||
buildConfigField "boolean", "DEVELOPER_MODE", "true"
|
||||
}
|
||||
}
|
||||
|
||||
// Do not abort build if lint finds errors
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
lintConfig file("$rootProject.projectDir/config/lint/lint.xml")
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/LICENSE.txt'
|
||||
exclude 'META-INF/NOTICE'
|
||||
exclude 'META-INF/NOTICE.txt'
|
||||
exclude 'META-INF/README'
|
||||
exclude 'LICENSE.txt'
|
||||
exclude 'META-INF/*.kotlin_module'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility javaVersion
|
||||
targetCompatibility javaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = kotlinJvmVersion
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty('keyAlias')) {
|
||||
android.signingConfigs.release.keyAlias = keyAlias
|
||||
}
|
||||
if (project.hasProperty('keyPassword')) {
|
||||
android.signingConfigs.release.keyPassword = keyPassword
|
||||
}
|
||||
if (project.hasProperty('storeFile')) {
|
||||
android.signingConfigs.release.storeFile = file(storeFile)
|
||||
}
|
||||
if (project.hasProperty('storePassword')) {
|
||||
android.signingConfigs.release.storePassword = storePassword
|
||||
}
|
52
app/k9mail-jmap/proguard-rules.pro
vendored
52
app/k9mail-jmap/proguard-rules.pro
vendored
|
@ -1,52 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
# Preserve the line number information for debugging stack traces.
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# Library specific rules
|
||||
-dontnote android.net.http.*
|
||||
-dontnote org.apache.commons.codec.**
|
||||
-dontnote org.apache.http.**
|
||||
-dontnote com.squareup.moshi.**
|
||||
-dontnote com.github.amlcurran.showcaseview.**
|
||||
-dontnote de.cketti.safecontentresolver.**
|
||||
-dontnote com.tokenautocomplete.**
|
||||
-keep class rs.ltt.jmap.common.** {*;}
|
||||
|
||||
-dontwarn okio.**
|
||||
-dontwarn com.squareup.moshi.**
|
||||
|
||||
# Glide
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep public class * extends com.bumptech.glide.module.LibraryGlideModule
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
# Project specific rules
|
||||
-dontnote com.fsck.k9.ui.messageview.**
|
||||
-dontnote com.fsck.k9.view.**
|
||||
|
||||
-keep public class org.openintents.openpgp.**
|
||||
|
||||
-keepclassmembers class * extends androidx.appcompat.widget.SearchView {
|
||||
public <init>(android.content.Context);
|
||||
}
|
||||
|
||||
# okhttp rules
|
||||
# see: https://github.com/square/okhttp/blob/master/okhttp/src/main/resources/META-INF/proguard/okhttp3.pro
|
||||
|
||||
# JSR 305 annotations are for embedding nullability information.
|
||||
-dontwarn javax.annotation.**
|
||||
|
||||
# A resource is loaded with a relative path so the package of this class must be preserved.
|
||||
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
|
||||
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||
|
||||
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
|
||||
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
|
@ -1,300 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="auto"
|
||||
package="com.fsck.k9.jmap">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false"/>
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
||||
<application
|
||||
android:name="com.fsck.k9.App"
|
||||
android:allowTaskReparenting="false"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.K9.Startup"
|
||||
android:resizeableActivity="true"
|
||||
android:allowBackup="false"
|
||||
tools:replace="android:theme">
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value="com.fsck.k9.activity.Search"/>
|
||||
|
||||
<!-- TODO: Remove once minSdkVersion has been changed to 24+ -->
|
||||
<meta-data
|
||||
android:name="com.lge.support.SPLIT_WINDOW"
|
||||
android:value="true"/>
|
||||
<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"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.onboarding.OnboardingActivity"
|
||||
android:label="@string/welcome_message_title" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.settings.account.OpenPgpAppSelectDialog"
|
||||
android:configChanges="locale"
|
||||
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight"
|
||||
/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupBasics"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_setup_basics_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupAccountType"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_setup_account_type_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupIncoming"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_setup_incoming_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupComposition"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_settings_composition_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupOutgoing"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_setup_outgoing_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupOptions"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_setup_options_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupNames"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/account_setup_names_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.ChooseAccount"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/choose_account_title"
|
||||
android:noHistory="true" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.choosefolder.ChooseFolderActivity"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/choose_folder_title"
|
||||
android:noHistory="true" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.ChooseIdentity"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/choose_identity_title" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.ManageIdentities"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/manage_identities_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.EditIdentity"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/edit_identity_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.notification.DeleteConfirmationActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight"/>
|
||||
|
||||
<!-- XXX Note: this activity is hacked to ignore config changes,
|
||||
since it doesn't currently handle them correctly in code. -->
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.setup.AccountSetupCheckSettings"
|
||||
android:configChanges="keyboardHidden|orientation|locale"
|
||||
android:label="@string/account_setup_check_settings_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/ac_transfer_title"
|
||||
/>
|
||||
|
||||
<activity android:name="com.fsck.k9.activity.MessageList">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
||||
<!-- TODO: Remove once minSdkVersion has been changed to 24+ -->
|
||||
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
|
||||
<category android:name="android.intent.category.PENWINDOW_LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.MessageCompose"
|
||||
android:configChanges="locale"
|
||||
android:enabled="false"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SENDTO"/>
|
||||
<data android:scheme="mailto"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<data android:mimeType="*/*"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
||||
<data android:mimeType="*/*"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<data android:scheme="mailto"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="org.autocrypt.PEER_ACTION"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.service.chooser.chooser_target_service"
|
||||
android:value="com.fsck.k9.directshare.K9ChooserTargetService" />
|
||||
</activity>
|
||||
|
||||
<!-- Search Activity - searchable -->
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.Search"
|
||||
android:configChanges="locale"
|
||||
android:label="@string/search_action"
|
||||
android:uiOptions="splitActionBarWhenNarrow">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.activity.UpgradeDatabases"
|
||||
android:label="@string/upgrade_databases_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.managefolders.ManageFoldersActivity"
|
||||
android:label="@string/folders_action" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.settings.SettingsActivity"
|
||||
android:label="@string/prefs_title" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.settings.general.GeneralSettingsActivity"
|
||||
android:label="@string/general_settings_title" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.settings.account.AccountSettingsActivity"
|
||||
android:label="@string/account_settings_title_fmt" />
|
||||
|
||||
<activity
|
||||
android:name="com.fsck.k9.ui.addaccount.AddAccountActivity"
|
||||
android:label="@string/add_account_action" />
|
||||
|
||||
<service
|
||||
android:name="com.fsck.k9.notification.NotificationActionService"
|
||||
android:enabled="true"/>
|
||||
|
||||
<service
|
||||
android:name="com.fsck.k9.service.DatabaseUpgradeService"
|
||||
android:exported="false"/>
|
||||
|
||||
<service
|
||||
android:name="com.fsck.k9.account.AccountRemoverService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
||||
|
||||
<provider
|
||||
android:name="com.fsck.k9.provider.AttachmentProvider"
|
||||
android:authorities="${applicationId}.attachmentprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data
|
||||
android:name="de.cketti.safecontentresolver.ALLOW_INTERNAL_ACCESS"
|
||||
android:value="true" />
|
||||
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="com.fsck.k9.provider.RawMessageProvider"
|
||||
android:authorities="${applicationId}.rawmessageprovider"
|
||||
android:exported="false">
|
||||
|
||||
<meta-data
|
||||
android:name="de.cketti.safecontentresolver.ALLOW_INTERNAL_ACCESS"
|
||||
android:value="true" />
|
||||
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="com.fsck.k9.provider.EmailProvider"
|
||||
android:authorities="${applicationId}.provider.email"
|
||||
android:exported="false"/>
|
||||
|
||||
<provider
|
||||
android:name="com.fsck.k9.provider.DecryptedFileProvider"
|
||||
android:authorities="${applicationId}.decryptedfileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/decrypted_file_provider_paths" />
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="com.fsck.k9.provider.AttachmentTempFileProvider"
|
||||
android:authorities="${applicationId}.tempfileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/temp_file_provider_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
</manifest>
|
|
@ -1,35 +0,0 @@
|
|||
package com.fsck.k9
|
||||
|
||||
import android.app.Application
|
||||
import com.fsck.k9.activity.MessageCompose
|
||||
import com.fsck.k9.controller.MessagingController
|
||||
import com.fsck.k9.ui.base.ThemeManager
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class App : Application() {
|
||||
private val messagingController: MessagingController by inject()
|
||||
private val messagingListenerProvider: MessagingListenerProvider by inject()
|
||||
private val themeManager: ThemeManager by inject()
|
||||
|
||||
override fun onCreate() {
|
||||
Core.earlyInit()
|
||||
|
||||
super.onCreate()
|
||||
|
||||
DI.start(this, coreModules + uiModules + appModules)
|
||||
|
||||
K9.init(this)
|
||||
Core.init(this)
|
||||
themeManager.init()
|
||||
|
||||
messagingListenerProvider.listeners.forEach { listener ->
|
||||
messagingController.addListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val appConfig = AppConfig(
|
||||
componentsToDisable = listOf(MessageCompose::class.java)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.fsck.k9
|
||||
|
||||
import com.fsck.k9.backends.backendsModule
|
||||
import com.fsck.k9.controller.ControllerExtension
|
||||
import com.fsck.k9.crypto.EncryptionExtractor
|
||||
import com.fsck.k9.crypto.openpgp.OpenPgpEncryptionExtractor
|
||||
import com.fsck.k9.notification.notificationModule
|
||||
import com.fsck.k9.preferences.K9StoragePersister
|
||||
import com.fsck.k9.preferences.StoragePersister
|
||||
import com.fsck.k9.resources.resourcesModule
|
||||
import com.fsck.k9.storage.storageModule
|
||||
import com.fsck.k9.ui.addaccount.uiAddAccountModule
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
private val mainAppModule = module {
|
||||
single { App.appConfig }
|
||||
single { MessagingListenerProvider(emptyList()) }
|
||||
single(named("controllerExtensions")) { emptyList<ControllerExtension>() }
|
||||
single<EncryptionExtractor> { OpenPgpEncryptionExtractor.newInstance() }
|
||||
single<StoragePersister> { K9StoragePersister(get()) }
|
||||
}
|
||||
|
||||
val appModules = listOf(
|
||||
mainAppModule,
|
||||
notificationModule,
|
||||
resourcesModule,
|
||||
backendsModule,
|
||||
storageModule,
|
||||
uiAddAccountModule
|
||||
)
|
|
@ -1,5 +0,0 @@
|
|||
package com.fsck.k9
|
||||
|
||||
import com.fsck.k9.controller.MessagingListener
|
||||
|
||||
class MessagingListenerProvider(val listeners: List<MessagingListener>)
|
|
@ -1,72 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import com.fsck.k9.backend.imap.SystemAlarmManager
|
||||
import com.fsck.k9.helper.AlarmManagerCompat
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
private const val ALARM_ACTION = "com.fsck.k9.backends.ALARM"
|
||||
private const val REQUEST_CODE = 1
|
||||
|
||||
private typealias Callback = () -> Unit
|
||||
|
||||
class AndroidAlarmManager(
|
||||
private val context: Context,
|
||||
private val alarmManager: AlarmManagerCompat,
|
||||
backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) : SystemAlarmManager {
|
||||
private val coroutineScope = CoroutineScope(backgroundDispatcher)
|
||||
|
||||
private val pendingIntent: PendingIntent = run {
|
||||
val intent = Intent(ALARM_ACTION).apply {
|
||||
setPackage(context.packageName)
|
||||
}
|
||||
val flags = if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0
|
||||
|
||||
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, flags)
|
||||
}
|
||||
|
||||
private val callback = AtomicReference<Callback?>(null)
|
||||
|
||||
init {
|
||||
val intentFilter = IntentFilter(ALARM_ACTION)
|
||||
context.registerReceiver(
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val callback = callback.getAndSet(null)
|
||||
if (callback == null) {
|
||||
Timber.w("Alarm triggered but 'callback' was null")
|
||||
} else {
|
||||
coroutineScope.launch {
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
intentFilter
|
||||
)
|
||||
}
|
||||
|
||||
override fun setAlarm(triggerTime: Long, callback: Callback) {
|
||||
this.callback.set(callback)
|
||||
alarmManager.scheduleAlarm(triggerTime, pendingIntent)
|
||||
}
|
||||
|
||||
override fun cancelAlarm() {
|
||||
callback.set(null)
|
||||
alarmManager.cancelAlarm(pendingIntent)
|
||||
}
|
||||
|
||||
override fun now(): Long = SystemClock.elapsedRealtime()
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.backend.BackendFactory
|
||||
import com.fsck.k9.backend.api.Backend
|
||||
import com.fsck.k9.backend.imap.ImapBackend
|
||||
import com.fsck.k9.backend.imap.ImapPushConfigProvider
|
||||
import com.fsck.k9.mail.NetworkType
|
||||
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
|
||||
import com.fsck.k9.mail.power.PowerManager
|
||||
import com.fsck.k9.mail.ssl.TrustedSocketFactory
|
||||
import com.fsck.k9.mail.store.imap.IdleRefreshManager
|
||||
import com.fsck.k9.mail.store.imap.ImapStore
|
||||
import com.fsck.k9.mail.store.imap.ImapStoreConfig
|
||||
import com.fsck.k9.mail.transport.smtp.SmtpTransport
|
||||
import com.fsck.k9.mailstore.K9BackendStorageFactory
|
||||
import com.fsck.k9.preferences.AccountManager
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class ImapBackendFactory(
|
||||
private val context: Context,
|
||||
private val accountManager: AccountManager,
|
||||
private val powerManager: PowerManager,
|
||||
private val idleRefreshManager: IdleRefreshManager,
|
||||
private val backendStorageFactory: K9BackendStorageFactory,
|
||||
private val trustedSocketFactory: TrustedSocketFactory
|
||||
) : BackendFactory {
|
||||
override fun createBackend(account: Account): Backend {
|
||||
val accountName = account.displayName
|
||||
val backendStorage = backendStorageFactory.createBackendStorage(account)
|
||||
val imapStore = createImapStore(account)
|
||||
val pushConfigProvider = createPushConfigProvider(account)
|
||||
val smtpTransport = createSmtpTransport(account)
|
||||
|
||||
return ImapBackend(
|
||||
accountName,
|
||||
backendStorage,
|
||||
imapStore,
|
||||
powerManager,
|
||||
idleRefreshManager,
|
||||
pushConfigProvider,
|
||||
smtpTransport
|
||||
)
|
||||
}
|
||||
|
||||
private fun createImapStore(account: Account): ImapStore {
|
||||
val oAuth2TokenProvider: OAuth2TokenProvider? = null
|
||||
val config = createImapStoreConfig(account)
|
||||
return ImapStore.create(
|
||||
account.incomingServerSettings,
|
||||
config,
|
||||
trustedSocketFactory,
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
|
||||
oAuth2TokenProvider
|
||||
)
|
||||
}
|
||||
|
||||
private fun createImapStoreConfig(account: Account): ImapStoreConfig {
|
||||
return object : ImapStoreConfig {
|
||||
override val logLabel
|
||||
get() = account.description
|
||||
|
||||
override fun isSubscribedFoldersOnly() = account.isSubscribedFoldersOnly
|
||||
|
||||
override fun useCompression(type: NetworkType) = account.useCompression(type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSmtpTransport(account: Account): SmtpTransport {
|
||||
val serverSettings = account.outgoingServerSettings
|
||||
val oauth2TokenProvider: OAuth2TokenProvider? = null
|
||||
return SmtpTransport(serverSettings, trustedSocketFactory, oauth2TokenProvider)
|
||||
}
|
||||
|
||||
private fun createPushConfigProvider(account: Account) = object : ImapPushConfigProvider {
|
||||
override val maxPushFoldersFlow: Flow<Int>
|
||||
get() = accountManager.getAccountFlow(account.uuid)
|
||||
.map { it.maxPushFolders }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val idleRefreshMinutesFlow: Flow<Int>
|
||||
get() = accountManager.getAccountFlow(account.uuid)
|
||||
.map { it.idleRefreshMinutes }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.account.AccountCreator
|
||||
import com.fsck.k9.backend.BackendManager
|
||||
import com.fsck.k9.backend.jmap.JmapDiscoveryResult.JmapAccount
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import com.fsck.k9.mail.FolderType
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import com.fsck.k9.mailstore.LocalStoreProvider
|
||||
|
||||
class JmapAccountCreator(
|
||||
private val preferences: Preferences,
|
||||
private val backendManager: BackendManager,
|
||||
private val accountCreator: AccountCreator,
|
||||
private val localStoreProvider: LocalStoreProvider
|
||||
) {
|
||||
fun createAccount(emailAddress: String, password: String, jmapAccount: JmapAccount) {
|
||||
val serverSettings = createServerSettings(emailAddress, password, jmapAccount)
|
||||
|
||||
val account = preferences.newAccount().apply {
|
||||
email = emailAddress
|
||||
description = jmapAccount.name
|
||||
incomingServerSettings = serverSettings
|
||||
outgoingServerSettings = serverSettings
|
||||
|
||||
chipColor = accountCreator.pickColor()
|
||||
deletePolicy = Account.DeletePolicy.ON_DELETE
|
||||
}
|
||||
|
||||
createOutboxFolder(account)
|
||||
preferences.saveAccount(account)
|
||||
|
||||
fetchFolderList(account)
|
||||
}
|
||||
|
||||
private fun createServerSettings(emailAddress: String, password: String, jmapAccount: JmapAccount): ServerSettings {
|
||||
return ServerSettings(
|
||||
"jmap",
|
||||
null,
|
||||
433,
|
||||
ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
AuthType.PLAIN,
|
||||
emailAddress,
|
||||
password,
|
||||
null,
|
||||
mapOf("accountId" to jmapAccount.accountId)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createOutboxFolder(account: Account) {
|
||||
val localStore = localStoreProvider.getInstance(account)
|
||||
account.outboxFolderId = localStore.createLocalFolder(Account.OUTBOX_NAME, FolderType.OUTBOX)
|
||||
}
|
||||
|
||||
private fun fetchFolderList(account: Account) {
|
||||
val backend = backendManager.getBackend(account)
|
||||
backend.refreshFolderList()
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.backend.BackendFactory
|
||||
import com.fsck.k9.backend.api.Backend
|
||||
import com.fsck.k9.backend.jmap.JmapBackend
|
||||
import com.fsck.k9.backend.jmap.JmapConfig
|
||||
import com.fsck.k9.mailstore.K9BackendStorageFactory
|
||||
|
||||
class JmapBackendFactory(
|
||||
private val backendStorageFactory: K9BackendStorageFactory,
|
||||
private val okHttpClientProvider: OkHttpClientProvider
|
||||
) : BackendFactory {
|
||||
override fun createBackend(account: Account): Backend {
|
||||
val backendStorage = backendStorageFactory.createBackendStorage(account)
|
||||
val okHttpClient = okHttpClientProvider.getOkHttpClient()
|
||||
|
||||
val serverSettings = account.incomingServerSettings
|
||||
val jmapConfig = JmapConfig(
|
||||
username = serverSettings.username,
|
||||
password = serverSettings.password!!,
|
||||
baseUrl = serverSettings.host,
|
||||
accountId = serverSettings.extra["accountId"]!!
|
||||
)
|
||||
|
||||
return JmapBackend(backendStorage, okHttpClient, jmapConfig)
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import com.fsck.k9.backend.BackendManager
|
||||
import com.fsck.k9.backend.imap.BackendIdleRefreshManager
|
||||
import com.fsck.k9.backend.imap.SystemAlarmManager
|
||||
import com.fsck.k9.backend.jmap.JmapAccountDiscovery
|
||||
import com.fsck.k9.mail.store.imap.IdleRefreshManager
|
||||
import org.koin.dsl.module
|
||||
|
||||
val backendsModule = module {
|
||||
single {
|
||||
BackendManager(
|
||||
mapOf(
|
||||
"imap" to get<ImapBackendFactory>(),
|
||||
"pop3" to get<Pop3BackendFactory>(),
|
||||
"webdav" to get<WebDavBackendFactory>(),
|
||||
"jmap" to get<JmapBackendFactory>()
|
||||
)
|
||||
)
|
||||
}
|
||||
single {
|
||||
ImapBackendFactory(
|
||||
context = get(),
|
||||
accountManager = get(),
|
||||
powerManager = get(),
|
||||
idleRefreshManager = get(),
|
||||
backendStorageFactory = get(),
|
||||
trustedSocketFactory = get()
|
||||
)
|
||||
}
|
||||
single<SystemAlarmManager> { AndroidAlarmManager(context = get(), alarmManager = get()) }
|
||||
single<IdleRefreshManager> { BackendIdleRefreshManager(alarmManager = get()) }
|
||||
single { Pop3BackendFactory(get(), get()) }
|
||||
single { WebDavBackendFactory(get(), get(), get()) }
|
||||
single { JmapBackendFactory(get(), get()) }
|
||||
factory { JmapAccountDiscovery() }
|
||||
factory { JmapAccountCreator(get(), get(), get(), get()) }
|
||||
single { OkHttpClientProvider() }
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class OkHttpClientProvider {
|
||||
private var okHttpClient: OkHttpClient? = null
|
||||
|
||||
@Synchronized
|
||||
fun getOkHttpClient(): OkHttpClient {
|
||||
return okHttpClient ?: createOkHttpClient().also { okHttpClient = it }
|
||||
}
|
||||
|
||||
private fun createOkHttpClient(): OkHttpClient {
|
||||
return OkHttpClient.Builder().build()
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.backend.BackendFactory
|
||||
import com.fsck.k9.backend.api.Backend
|
||||
import com.fsck.k9.backend.pop3.Pop3Backend
|
||||
import com.fsck.k9.mail.oauth.OAuth2TokenProvider
|
||||
import com.fsck.k9.mail.ssl.TrustedSocketFactory
|
||||
import com.fsck.k9.mail.store.pop3.Pop3Store
|
||||
import com.fsck.k9.mail.transport.smtp.SmtpTransport
|
||||
import com.fsck.k9.mailstore.K9BackendStorageFactory
|
||||
|
||||
class Pop3BackendFactory(
|
||||
private val backendStorageFactory: K9BackendStorageFactory,
|
||||
private val trustedSocketFactory: TrustedSocketFactory
|
||||
) : BackendFactory {
|
||||
override fun createBackend(account: Account): Backend {
|
||||
val accountName = account.displayName
|
||||
val backendStorage = backendStorageFactory.createBackendStorage(account)
|
||||
val pop3Store = createPop3Store(account)
|
||||
val smtpTransport = createSmtpTransport(account)
|
||||
return Pop3Backend(accountName, backendStorage, pop3Store, smtpTransport)
|
||||
}
|
||||
|
||||
private fun createPop3Store(account: Account): Pop3Store {
|
||||
val serverSettings = account.incomingServerSettings
|
||||
return Pop3Store(serverSettings, trustedSocketFactory)
|
||||
}
|
||||
|
||||
private fun createSmtpTransport(account: Account): SmtpTransport {
|
||||
val serverSettings = account.outgoingServerSettings
|
||||
val oauth2TokenProvider: OAuth2TokenProvider? = null
|
||||
return SmtpTransport(serverSettings, trustedSocketFactory, oauth2TokenProvider)
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package com.fsck.k9.backends
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.backend.BackendFactory
|
||||
import com.fsck.k9.backend.api.Backend
|
||||
import com.fsck.k9.backend.webdav.WebDavBackend
|
||||
import com.fsck.k9.mail.ssl.TrustManagerFactory
|
||||
import com.fsck.k9.mail.store.webdav.DraftsFolderProvider
|
||||
import com.fsck.k9.mail.store.webdav.WebDavStore
|
||||
import com.fsck.k9.mail.transport.WebDavTransport
|
||||
import com.fsck.k9.mailstore.FolderRepository
|
||||
import com.fsck.k9.mailstore.K9BackendStorageFactory
|
||||
|
||||
class WebDavBackendFactory(
|
||||
private val backendStorageFactory: K9BackendStorageFactory,
|
||||
private val trustManagerFactory: TrustManagerFactory,
|
||||
private val folderRepository: FolderRepository
|
||||
) : BackendFactory {
|
||||
override fun createBackend(account: Account): Backend {
|
||||
val accountName = account.displayName
|
||||
val backendStorage = backendStorageFactory.createBackendStorage(account)
|
||||
val serverSettings = account.incomingServerSettings
|
||||
val draftsFolderProvider = createDraftsFolderProvider(account)
|
||||
val webDavStore = WebDavStore(trustManagerFactory, serverSettings, draftsFolderProvider)
|
||||
val webDavTransport = WebDavTransport(trustManagerFactory, serverSettings, draftsFolderProvider)
|
||||
return WebDavBackend(accountName, backendStorage, webDavStore, webDavTransport)
|
||||
}
|
||||
|
||||
private fun createDraftsFolderProvider(account: Account): DraftsFolderProvider {
|
||||
return DraftsFolderProvider {
|
||||
val draftsFolderId = account.draftsFolderId ?: error("No Drafts folder configured")
|
||||
folderRepository.getFolderServerId(account, draftsFolderId) ?: error("Couldn't find local Drafts folder")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package com.fsck.k9.glide;
|
||||
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
|
||||
@GlideModule
|
||||
public class K9AppGlideModule extends AppGlideModule {
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.K9
|
||||
import com.fsck.k9.activity.MessageList
|
||||
import com.fsck.k9.activity.compose.MessageActions
|
||||
import com.fsck.k9.activity.setup.AccountSetupIncoming
|
||||
import com.fsck.k9.activity.setup.AccountSetupOutgoing
|
||||
import com.fsck.k9.controller.MessageReference
|
||||
import com.fsck.k9.search.LocalSearch
|
||||
import com.fsck.k9.ui.messagelist.DefaultFolderProvider
|
||||
import com.fsck.k9.ui.notification.DeleteConfirmationActivity
|
||||
|
||||
/**
|
||||
* This class contains methods to create the [PendingIntent]s for the actions of our notifications.
|
||||
*
|
||||
* **Note:**
|
||||
* We need to take special care to ensure the `PendingIntent`s are unique as defined in the documentation of
|
||||
* [PendingIntent]. Otherwise selecting a notification action might perform the action on the wrong message.
|
||||
*
|
||||
* We use the notification ID as `requestCode` argument to ensure each notification/action pair gets a unique
|
||||
* `PendingIntent`.
|
||||
*/
|
||||
internal class K9NotificationActionCreator(
|
||||
private val context: Context,
|
||||
private val defaultFolderProvider: DefaultFolderProvider
|
||||
) : NotificationActionCreator {
|
||||
|
||||
override fun createViewMessagePendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = createMessageViewIntent(messageReference)
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createViewFolderPendingIntent(account: Account, folderId: Long, notificationId: Int): PendingIntent {
|
||||
val intent = createMessageListIntent(account, folderId)
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createViewMessagesPendingIntent(
|
||||
account: Account,
|
||||
messageReferences: List<MessageReference>,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val folderServerId = getFolderIdOfAllMessages(messageReferences)
|
||||
val intent = if (folderServerId != null) {
|
||||
createMessageListIntent(account, folderServerId)
|
||||
} else {
|
||||
createMessageListIntent(account)
|
||||
}
|
||||
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createViewFolderListPendingIntent(account: Account, notificationId: Int): PendingIntent {
|
||||
val intent = createMessageListIntent(account)
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createDismissAllMessagesPendingIntent(account: Account, notificationId: Int): PendingIntent {
|
||||
val intent = NotificationActionService.createDismissAllMessagesIntent(context, account)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createDismissMessagePendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = NotificationActionService.createDismissMessageIntent(context, messageReference)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createReplyPendingIntent(messageReference: MessageReference, notificationId: Int): PendingIntent {
|
||||
val intent = MessageActions.getActionReplyIntent(context, messageReference)
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createMarkMessageAsReadPendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = NotificationActionService.createMarkMessageAsReadIntent(context, messageReference)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createMarkAllAsReadPendingIntent(
|
||||
account: Account,
|
||||
messageReferences: List<MessageReference>,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val accountUuid = account.uuid
|
||||
val intent = NotificationActionService.createMarkAllAsReadIntent(context, accountUuid, messageReferences)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun getEditIncomingServerSettingsIntent(account: Account): PendingIntent {
|
||||
val intent = AccountSetupIncoming.intentActionEditIncomingSettings(context, account)
|
||||
return PendingIntent.getActivity(context, account.accountNumber, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun getEditOutgoingServerSettingsIntent(account: Account): PendingIntent {
|
||||
val intent = AccountSetupOutgoing.intentActionEditOutgoingSettings(context, account)
|
||||
return PendingIntent.getActivity(context, account.accountNumber, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createDeleteMessagePendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
return if (K9.isConfirmDeleteFromNotification) {
|
||||
createDeleteConfirmationPendingIntent(messageReference, notificationId)
|
||||
} else {
|
||||
createDeleteServicePendingIntent(messageReference, notificationId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDeleteServicePendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = NotificationActionService.createDeleteMessageIntent(context, messageReference)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
private fun createDeleteConfirmationPendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = DeleteConfirmationActivity.getIntent(context, messageReference)
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createDeleteAllPendingIntent(
|
||||
account: Account,
|
||||
messageReferences: List<MessageReference>,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
return if (K9.isConfirmDeleteFromNotification) {
|
||||
getDeleteAllConfirmationPendingIntent(messageReferences, notificationId)
|
||||
} else {
|
||||
getDeleteAllServicePendingIntent(account, messageReferences, notificationId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDeleteAllConfirmationPendingIntent(
|
||||
messageReferences: List<MessageReference>,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = DeleteConfirmationActivity.getIntent(context, messageReferences)
|
||||
return PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_CANCEL_CURRENT)
|
||||
}
|
||||
|
||||
private fun getDeleteAllServicePendingIntent(
|
||||
account: Account,
|
||||
messageReferences: List<MessageReference>,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val accountUuid = account.uuid
|
||||
val intent = NotificationActionService.createDeleteAllMessagesIntent(context, accountUuid, messageReferences)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createArchiveMessagePendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = NotificationActionService.createArchiveMessageIntent(context, messageReference)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createArchiveAllPendingIntent(
|
||||
account: Account,
|
||||
messageReferences: List<MessageReference>,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = NotificationActionService.createArchiveAllIntent(context, account, messageReferences)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
override fun createMarkMessageAsSpamPendingIntent(
|
||||
messageReference: MessageReference,
|
||||
notificationId: Int
|
||||
): PendingIntent {
|
||||
val intent = NotificationActionService.createMarkMessageAsSpamIntent(context, messageReference)
|
||||
return PendingIntent.getService(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
private fun createMessageListIntent(account: Account): Intent {
|
||||
val folderId = defaultFolderProvider.getDefaultFolder(account)
|
||||
val search = LocalSearch().apply {
|
||||
addAllowedFolder(folderId)
|
||||
addAccountUuid(account.uuid)
|
||||
}
|
||||
|
||||
return MessageList.intentDisplaySearch(
|
||||
context = context,
|
||||
search = search,
|
||||
noThreading = false,
|
||||
newTask = true,
|
||||
clearTop = true
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMessageListIntent(account: Account, folderId: Long): Intent {
|
||||
val search = LocalSearch().apply {
|
||||
addAllowedFolder(folderId)
|
||||
addAccountUuid(account.uuid)
|
||||
}
|
||||
|
||||
return MessageList.intentDisplaySearch(
|
||||
context = context,
|
||||
search = search,
|
||||
noThreading = false,
|
||||
newTask = true,
|
||||
clearTop = true
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMessageViewIntent(message: MessageReference): Intent {
|
||||
return MessageList.actionDisplayMessageIntent(context, message)
|
||||
}
|
||||
|
||||
private fun getFolderIdOfAllMessages(messageReferences: List<MessageReference>): Long? {
|
||||
val firstMessage = messageReferences.first()
|
||||
val folderId = firstMessage.folderId
|
||||
|
||||
return if (messageReferences.all { it.folderId == folderId }) folderId else null
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import android.content.Context
|
||||
import com.fsck.k9.jmap.R
|
||||
|
||||
class K9NotificationResourceProvider(private val context: Context) : NotificationResourceProvider {
|
||||
override val iconWarning: Int = R.drawable.notification_icon_warning
|
||||
override val iconMarkAsRead: Int = R.drawable.notification_action_mark_as_read
|
||||
override val iconDelete: Int = R.drawable.notification_action_delete
|
||||
override val iconReply: Int = R.drawable.notification_action_reply
|
||||
override val iconNewMail: Int = R.drawable.notification_icon_new_mail
|
||||
override val iconSendingMail: Int = R.drawable.notification_icon_check_mail
|
||||
override val iconCheckingMail: Int = R.drawable.notification_icon_check_mail
|
||||
override val wearIconMarkAsRead: Int = R.drawable.notification_action_mark_as_read
|
||||
override val wearIconDelete: Int = R.drawable.notification_action_delete
|
||||
override val wearIconArchive: Int = R.drawable.notification_action_archive
|
||||
override val wearIconReplyAll: Int = R.drawable.notification_action_reply
|
||||
override val wearIconMarkAsSpam: Int = R.drawable.notification_action_mark_as_spam
|
||||
|
||||
override val pushChannelName: String
|
||||
get() = context.getString(R.string.notification_channel_push_title)
|
||||
override val pushChannelDescription: String
|
||||
get() = context.getString(R.string.notification_channel_push_description)
|
||||
override val messagesChannelName: String
|
||||
get() = context.getString(R.string.notification_channel_messages_title)
|
||||
override val messagesChannelDescription: String
|
||||
get() = context.getString(R.string.notification_channel_messages_description)
|
||||
override val miscellaneousChannelName: String
|
||||
get() = context.getString(R.string.notification_channel_miscellaneous_title)
|
||||
override val miscellaneousChannelDescription: String
|
||||
get() = context.getString(R.string.notification_channel_miscellaneous_description)
|
||||
|
||||
override fun authenticationErrorTitle(): String =
|
||||
context.getString(R.string.notification_authentication_error_title)
|
||||
|
||||
override fun authenticationErrorBody(accountName: String): String =
|
||||
context.getString(R.string.notification_authentication_error_text, accountName)
|
||||
|
||||
override fun certificateErrorTitle(): String = context.getString(R.string.notification_certificate_error_public)
|
||||
|
||||
override fun certificateErrorTitle(accountName: String): String =
|
||||
context.getString(R.string.notification_certificate_error_title, accountName)
|
||||
|
||||
override fun certificateErrorBody(): String = context.getString(R.string.notification_certificate_error_text)
|
||||
|
||||
override fun newMailTitle(): String = context.getString(R.string.notification_new_title)
|
||||
|
||||
override fun newMailUnreadMessageCount(unreadMessageCount: Int, accountName: String): String =
|
||||
context.getString(R.string.notification_new_one_account_fmt, unreadMessageCount, accountName)
|
||||
|
||||
override fun newMessagesTitle(newMessagesCount: Int): String =
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.notification_new_messages_title,
|
||||
newMessagesCount, newMessagesCount
|
||||
)
|
||||
|
||||
override fun additionalMessages(overflowMessagesCount: Int, accountName: String): String =
|
||||
context.getString(R.string.notification_additional_messages, overflowMessagesCount, accountName)
|
||||
|
||||
override fun previewEncrypted(): String = context.getString(R.string.preview_encrypted)
|
||||
|
||||
override fun noSubject(): String = context.getString(R.string.general_no_subject)
|
||||
|
||||
override fun recipientDisplayName(recipientDisplayName: String): String =
|
||||
context.getString(R.string.message_to_fmt, recipientDisplayName)
|
||||
|
||||
override fun noSender(): String = context.getString(R.string.general_no_sender)
|
||||
|
||||
override fun sendFailedTitle(): String = context.getString(R.string.send_failure_subject)
|
||||
|
||||
override fun sendingMailTitle(): String = context.getString(R.string.notification_bg_send_title)
|
||||
|
||||
override fun sendingMailBody(accountName: String): String =
|
||||
context.getString(R.string.notification_bg_send_ticker, accountName)
|
||||
|
||||
override fun checkingMailTicker(accountName: String, folderName: String): String =
|
||||
context.getString(R.string.notification_bg_sync_ticker, accountName, folderName)
|
||||
|
||||
override fun checkingMailTitle(): String =
|
||||
context.getString(R.string.notification_bg_sync_title)
|
||||
|
||||
override fun checkingMailSeparator(): String =
|
||||
context.getString(R.string.notification_bg_title_separator)
|
||||
|
||||
override fun actionMarkAsRead(): String = context.getString(R.string.notification_action_mark_as_read)
|
||||
|
||||
override fun actionMarkAllAsRead(): String = context.getString(R.string.notification_action_mark_all_as_read)
|
||||
|
||||
override fun actionDelete(): String = context.getString(R.string.notification_action_delete)
|
||||
|
||||
override fun actionDeleteAll(): String = context.getString(R.string.notification_action_delete_all)
|
||||
|
||||
override fun actionReply(): String = context.getString(R.string.notification_action_reply)
|
||||
|
||||
override fun actionArchive(): String = context.getString(R.string.notification_action_archive)
|
||||
|
||||
override fun actionArchiveAll(): String = context.getString(R.string.notification_action_archive_all)
|
||||
|
||||
override fun actionMarkAsSpam(): String = context.getString(R.string.notification_action_spam)
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.K9
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.mail.Flag
|
||||
import com.fsck.k9.mailstore.LocalFolder
|
||||
import com.fsck.k9.mailstore.LocalMessage
|
||||
|
||||
class K9NotificationStrategy(val contacts: Contacts) : NotificationStrategy {
|
||||
|
||||
override fun shouldNotifyForMessage(
|
||||
account: Account,
|
||||
localFolder: LocalFolder,
|
||||
message: LocalMessage,
|
||||
isOldMessage: Boolean
|
||||
): Boolean {
|
||||
|
||||
// If we don't even have an account name, don't show the notification.
|
||||
// (This happens during initial account setup)
|
||||
if (account.name == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (K9.isQuietTime && !K9.isNotificationDuringQuietTimeEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Do not notify if the user does not have notifications enabled or if the message has
|
||||
// been read.
|
||||
if (!account.isNotifyNewMail || message.isSet(Flag.SEEN) || isOldMessage) {
|
||||
return false
|
||||
}
|
||||
|
||||
val aDisplayMode = account.folderDisplayMode
|
||||
val aNotifyMode = account.folderNotifyNewMailMode
|
||||
val fDisplayClass = localFolder.displayClass
|
||||
val fNotifyClass = localFolder.notifyClass
|
||||
|
||||
if (LocalFolder.isModeMismatch(aDisplayMode, fDisplayClass)) {
|
||||
// Never notify a folder that isn't displayed
|
||||
return false
|
||||
}
|
||||
|
||||
if (LocalFolder.isModeMismatch(aNotifyMode, fNotifyClass)) {
|
||||
// Do not notify folders in the wrong class
|
||||
return false
|
||||
}
|
||||
|
||||
// No notification for new messages in Trash, Drafts, Spam or Sent folder.
|
||||
val folder = message.folder
|
||||
if (folder != null) {
|
||||
val folderId = folder.databaseId
|
||||
if (folderId == account.trashFolderId ||
|
||||
folderId == account.draftsFolderId ||
|
||||
folderId == account.spamFolderId ||
|
||||
folderId == account.sentFolderId
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Don't notify if the sender address matches one of our identities and the user chose not
|
||||
// to be notified for such messages.
|
||||
return if (account.isAnIdentity(message.from) && !account.isNotifySelfNewMail) {
|
||||
false
|
||||
} else !account.isNotifyContactsMailOnly || contacts.isAnyInContacts(message.from)
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import org.koin.dsl.module
|
||||
|
||||
val notificationModule = module {
|
||||
single<NotificationActionCreator> { K9NotificationActionCreator(context = get(), defaultFolderProvider = get()) }
|
||||
single<NotificationResourceProvider> { K9NotificationResourceProvider(get()) }
|
||||
single<NotificationStrategy> { K9NotificationStrategy(get()) }
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package com.fsck.k9.resources
|
||||
|
||||
import android.content.Context
|
||||
import com.fsck.k9.autocrypt.AutocryptStringProvider
|
||||
import com.fsck.k9.jmap.R
|
||||
|
||||
class K9AutocryptStringProvider(private val context: Context) : AutocryptStringProvider {
|
||||
override fun transferMessageSubject(): String = context.getString(R.string.ac_transfer_msg_subject)
|
||||
override fun transferMessageBody(): String = context.getString(R.string.ac_transfer_msg_body)
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package com.fsck.k9.resources
|
||||
|
||||
import android.content.Context
|
||||
import com.fsck.k9.CoreResourceProvider
|
||||
import com.fsck.k9.jmap.R
|
||||
import com.fsck.k9.notification.PushNotificationState
|
||||
|
||||
class K9CoreResourceProvider(private val context: Context) : CoreResourceProvider {
|
||||
override fun defaultSignature(): String = context.getString(R.string.default_signature)
|
||||
override fun defaultIdentityDescription(): String = context.getString(R.string.default_identity_description)
|
||||
|
||||
override fun contactDisplayNamePrefix(): String = context.getString(R.string.message_to_label)
|
||||
override fun contactUnknownSender(): String = context.getString(R.string.unknown_sender)
|
||||
override fun contactUnknownRecipient(): String = context.getString(R.string.unknown_recipient)
|
||||
|
||||
override fun messageHeaderFrom(): String = context.getString(R.string.message_compose_quote_header_from)
|
||||
override fun messageHeaderTo(): String = context.getString(R.string.message_compose_quote_header_to)
|
||||
override fun messageHeaderCc(): String = context.getString(R.string.message_compose_quote_header_cc)
|
||||
override fun messageHeaderDate(): String = context.getString(R.string.message_compose_quote_header_send_date)
|
||||
override fun messageHeaderSubject(): String = context.getString(R.string.message_compose_quote_header_subject)
|
||||
override fun messageHeaderSeparator(): String = context.getString(R.string.message_compose_quote_header_separator)
|
||||
|
||||
override fun noSubject(): String = context.getString(R.string.general_no_subject)
|
||||
|
||||
override fun userAgent(): String = context.getString(R.string.message_header_mua)
|
||||
override fun encryptedSubject(): String = context.getString(R.string.encrypted_subject)
|
||||
|
||||
override fun replyHeader(sender: String): String =
|
||||
context.getString(R.string.message_compose_reply_header_fmt, sender)
|
||||
|
||||
override fun replyHeader(sender: String, sentDate: String): String =
|
||||
context.getString(R.string.message_compose_reply_header_fmt_with_date, sentDate, sender)
|
||||
|
||||
override fun searchUnifiedInboxTitle(): String = context.getString(R.string.integrated_inbox_title)
|
||||
override fun searchUnifiedInboxDetail(): String = context.getString(R.string.integrated_inbox_detail)
|
||||
|
||||
override fun outboxFolderName(): String = context.getString(R.string.special_mailbox_name_outbox)
|
||||
|
||||
override val iconPushNotification: Int = R.drawable.ic_push_notification
|
||||
|
||||
override fun pushNotificationText(notificationState: PushNotificationState): String {
|
||||
val resId = when (notificationState) {
|
||||
PushNotificationState.INITIALIZING -> R.string.push_notification_state_initializing
|
||||
PushNotificationState.LISTENING -> R.string.push_notification_state_listening
|
||||
PushNotificationState.WAIT_BACKGROUND_SYNC -> R.string.push_notification_state_wait_background_sync
|
||||
PushNotificationState.WAIT_NETWORK -> R.string.push_notification_state_wait_network
|
||||
}
|
||||
return context.getString(resId)
|
||||
}
|
||||
|
||||
override fun pushNotificationInfoText(): String = context.getString(R.string.push_notification_info)
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package com.fsck.k9.resources
|
||||
|
||||
import com.fsck.k9.CoreResourceProvider
|
||||
import com.fsck.k9.autocrypt.AutocryptStringProvider
|
||||
import org.koin.dsl.module
|
||||
|
||||
val resourcesModule = module {
|
||||
single<CoreResourceProvider> { K9CoreResourceProvider(get()) }
|
||||
single<AutocryptStringProvider> { K9AutocryptStringProvider(get()) }
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package com.fsck.k9.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.BindingAdapter
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
@BindingAdapter("isVisible")
|
||||
fun setVisibility(view: View, value: Boolean) {
|
||||
view.isVisible = value
|
||||
}
|
||||
|
||||
@BindingAdapter("error")
|
||||
fun setError(view: TextInputLayout, value: Int?) {
|
||||
if (value == null) {
|
||||
view.error = null
|
||||
} else {
|
||||
val errorString = view.context.getString(value)
|
||||
view.error = errorString
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.fsck.k9.ui.addaccount
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import com.fsck.k9.jmap.R
|
||||
import com.fsck.k9.ui.base.K9Activity
|
||||
import com.fsck.k9.ui.base.extensions.findNavController
|
||||
|
||||
class AddAccountActivity : K9Activity() {
|
||||
private lateinit var navController: NavController
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setLayout(R.layout.activity_add_account)
|
||||
|
||||
initializeActionBar()
|
||||
}
|
||||
|
||||
private fun initializeActionBar() {
|
||||
val appBarConfiguration = AppBarConfiguration(topLevelDestinationIds = setOf(R.id.addJmapAccountScreen))
|
||||
|
||||
navController = findNavController(R.id.nav_host_fragment)
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
return navController.navigateUp() || super.onSupportNavigateUp()
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package com.fsck.k9.ui.addaccount
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.fsck.k9.jmap.R
|
||||
import com.fsck.k9.jmap.databinding.FragmentAddAccountBinding
|
||||
import com.fsck.k9.ui.observeNotNull
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class AddAccountFragment : Fragment() {
|
||||
private val viewModel: AddAccountViewModel by viewModel()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.getActionEvents().observeNotNull(this) { handleActionEvents(it) }
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val binding = FragmentAddAccountBinding.inflate(inflater, container, false)
|
||||
binding.lifecycleOwner = this
|
||||
binding.viewModel = viewModel
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun handleActionEvents(action: Action) {
|
||||
when (action) {
|
||||
is Action.GoToMessageList -> goToMessageList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToMessageList() {
|
||||
findNavController().navigate(R.id.action_addJmapAccountScreen_to_messageListScreen)
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
package com.fsck.k9.ui.addaccount
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.fsck.k9.EmailAddressValidator
|
||||
import com.fsck.k9.backend.jmap.JmapAccountDiscovery
|
||||
import com.fsck.k9.backend.jmap.JmapDiscoveryResult
|
||||
import com.fsck.k9.backend.jmap.JmapDiscoveryResult.JmapAccount
|
||||
import com.fsck.k9.backends.JmapAccountCreator
|
||||
import com.fsck.k9.helper.SingleLiveEvent
|
||||
import com.fsck.k9.helper.measureRealtimeMillisWithResult
|
||||
import com.fsck.k9.jmap.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AddAccountViewModel(
|
||||
private val emailAddressValidator: EmailAddressValidator,
|
||||
private val jmapAccountDiscovery: JmapAccountDiscovery,
|
||||
private val jmapAccountCreator: JmapAccountCreator
|
||||
) : ViewModel() {
|
||||
val emailAddress = MutableLiveData<String>()
|
||||
val emailAddressError = MutableLiveData<Int?>()
|
||||
val password = MutableLiveData<String>()
|
||||
val passwordError = MutableLiveData<Int?>()
|
||||
val setupErrorText: MutableLiveData<Int> = createMutableLiveData(R.string.empty_string)
|
||||
val isInputEnabled: MutableLiveData<Boolean> = createMutableLiveData(true)
|
||||
val isNextButtonEnabled: MutableLiveData<Boolean> = createMutableLiveData(true)
|
||||
val isProgressBarVisible: MutableLiveData<Boolean> = createMutableLiveData(false)
|
||||
private val actionLiveData = SingleLiveEvent<Action>()
|
||||
|
||||
init {
|
||||
Transformations.distinctUntilChanged(emailAddress).observeForever { resetEmailAddressError() }
|
||||
Transformations.distinctUntilChanged(password).observeForever { resetPasswordError() }
|
||||
}
|
||||
|
||||
fun getActionEvents(): LiveData<Action> = actionLiveData
|
||||
|
||||
fun onNextButtonClicked() {
|
||||
discoverServerSettings()
|
||||
}
|
||||
|
||||
private fun discoverServerSettings() {
|
||||
val emailAddress = this.emailAddress.value?.trim() ?: ""
|
||||
val password = this.password.value ?: ""
|
||||
|
||||
if (!emailAddressValidator.isValidAddressOnly(emailAddress)) {
|
||||
displayEmailAddressError(R.string.add_account__email_address_error)
|
||||
return
|
||||
}
|
||||
|
||||
showDiscoveryProgressBar()
|
||||
|
||||
viewModelScope.launch {
|
||||
val (elapsed, discoveryResult) = measureRealtimeMillisWithResult {
|
||||
withContext(Dispatchers.IO) {
|
||||
jmapAccountDiscovery.discover(emailAddress, password)
|
||||
}
|
||||
}
|
||||
|
||||
if (elapsed < MIN_PROGRESS_DURATION) {
|
||||
delay(MIN_PROGRESS_DURATION - elapsed)
|
||||
}
|
||||
|
||||
if (discoveryResult is JmapAccount) {
|
||||
createAccount(emailAddress, password, discoveryResult)
|
||||
} else {
|
||||
displayDiscoveryError(discoveryResult)
|
||||
hideDiscoveryProgressBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createAccount(emailAddress: String, password: String, jmapAccount: JmapAccount) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
jmapAccountCreator.createAccount(emailAddress, password, jmapAccount)
|
||||
}.join()
|
||||
|
||||
sendActionEvent(Action.GoToMessageList)
|
||||
}
|
||||
|
||||
private fun displayDiscoveryError(discoveryResult: JmapDiscoveryResult) {
|
||||
when (discoveryResult) {
|
||||
is JmapDiscoveryResult.GenericFailure -> {
|
||||
displayError(R.string.add_account__generic_failure)
|
||||
}
|
||||
is JmapDiscoveryResult.NoEmailAccountFoundFailure -> {
|
||||
displayError(R.string.add_account__no_email_account_found)
|
||||
}
|
||||
is JmapDiscoveryResult.AuthenticationFailure -> {
|
||||
displayPasswordError(R.string.add_account__password_error)
|
||||
}
|
||||
is JmapDiscoveryResult.EndpointNotFoundFailure -> {
|
||||
displayError(R.string.add_account__jmap_server_not_found)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun displayEmailAddressError(@StringRes error: Int) {
|
||||
emailAddressError.value = error
|
||||
}
|
||||
|
||||
private fun resetEmailAddressError() {
|
||||
emailAddressError.value = null
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun displayPasswordError(@StringRes error: Int) {
|
||||
passwordError.value = error
|
||||
}
|
||||
|
||||
private fun resetPasswordError() {
|
||||
passwordError.value = null
|
||||
}
|
||||
|
||||
private fun showDiscoveryProgressBar() {
|
||||
isInputEnabled.value = false
|
||||
isProgressBarVisible.value = true
|
||||
isNextButtonEnabled.value = false
|
||||
setupErrorText.value = R.string.empty_string
|
||||
}
|
||||
|
||||
private fun hideDiscoveryProgressBar() {
|
||||
isInputEnabled.value = true
|
||||
isProgressBarVisible.value = false
|
||||
isNextButtonEnabled.value = true
|
||||
}
|
||||
|
||||
private fun displayError(@StringRes error: Int) {
|
||||
setupErrorText.value = error
|
||||
}
|
||||
|
||||
private fun sendActionEvent(action: Action) {
|
||||
actionLiveData.value = action
|
||||
}
|
||||
|
||||
private fun <T> createMutableLiveData(initialValue: T): MutableLiveData<T> {
|
||||
return MutableLiveData<T>().apply {
|
||||
value = initialValue
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MIN_PROGRESS_DURATION = 500
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Action {
|
||||
object GoToMessageList : Action()
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package com.fsck.k9.ui.addaccount
|
||||
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val uiAddAccountModule = module {
|
||||
viewModel { AddAccountViewModel(get(), get(), get()) }
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/toolbar" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
app:defaultNavHost="true"
|
||||
app:navGraph="@navigation/navigation_add_account" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,117 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.fsck.k9.ui.addaccount.AddAccountViewModel" />
|
||||
</data>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/emailAddressLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:error="@{viewModel.emailAddressError}"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/emailAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="@{viewModel.isInputEnabled}"
|
||||
android:hint="@string/account_setup_basics_email_hint"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:inputType="textEmailAddress"
|
||||
android:text="@={viewModel.emailAddress}" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/emailPasswordLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:error="@{viewModel.passwordError}"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/emailAddressLayout"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/emailPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="@{viewModel.isInputEnabled}"
|
||||
android:hint="@string/account_setup_basics_password_hint"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:inputType="textPassword"
|
||||
android:text="@={viewModel.password}" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/setupError"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{viewModel.setupErrorText}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/emailPasswordLayout" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
app:isVisible="@{viewModel.isProgressBarVisible()}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/nextButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/setupError" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/nextButton"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:enabled="@{viewModel.isNextButtonEnabled()}"
|
||||
android:onClick="@{() -> viewModel.onNextButtonClicked()}"
|
||||
android:text="@string/next_action"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/emailPasswordLayout"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/navigation_add_account"
|
||||
app:startDestination="@id/addJmapAccountScreen">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/addJmapAccountScreen"
|
||||
android:name="com.fsck.k9.ui.addaccount.AddAccountFragment"
|
||||
android:label="@string/add_account_action"
|
||||
tools:layout="@layout/fragment_add_account">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_addJmapAccountScreen_to_messageListScreen"
|
||||
app:destination="@id/messageListScreen" />
|
||||
|
||||
</fragment>
|
||||
|
||||
<activity
|
||||
android:id="@+id/messageListScreen"
|
||||
android:name="com.fsck.k9.activity.MessageList"
|
||||
tools:layout="@layout/message_list"/>
|
||||
|
||||
</navigation>
|
|
@ -1,45 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/navigation_onboarding"
|
||||
app:startDestination="@id/welcomeScreen">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/welcomeScreen"
|
||||
android:name="com.fsck.k9.ui.onboarding.WelcomeFragment"
|
||||
android:label="@string/welcome_message_title"
|
||||
tools:layout="@layout/fragment_welcome_message">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_welcomeScreen_to_settingsImportScreen"
|
||||
app:destination="@id/settingsImportScreen" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_welcomeScreen_to_addAccountScreen"
|
||||
app:destination="@id/addAccountScreen" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_welcomeScreen_to_messageListScreen"
|
||||
app:destination="@id/messageListScreen" />
|
||||
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/settingsImportScreen"
|
||||
android:name="com.fsck.k9.ui.settings.import.SettingsImportFragment"
|
||||
android:label="@string/settings_import_title"
|
||||
tools:layout="@layout/fragment_settings_import"/>
|
||||
|
||||
<activity
|
||||
android:id="@+id/addAccountScreen"
|
||||
android:name="com.fsck.k9.ui.addaccount.AddAccountActivity"
|
||||
android:label="@string/add_account_action"
|
||||
tools:layout="@layout/activity_add_account"/>
|
||||
|
||||
<activity
|
||||
android:id="@+id/messageListScreen"
|
||||
android:name="com.fsck.k9.activity.MessageList"
|
||||
tools:layout="@layout/message_list"/>
|
||||
|
||||
</navigation>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="app_name">K-9 JMAP</string>
|
||||
<string name="empty_string" translatable="false" />
|
||||
|
||||
<string name="add_account__email_address_error">Please enter a valid email address</string>
|
||||
<string name="add_account__password_error">"Couldn't sign in. Please make sure your password is correct."</string>
|
||||
<string name="add_account__generic_failure">Unknown error while discovering server settings</string>
|
||||
<string name="add_account__no_email_account_found">"Couldn't find an email account on the JMAP server"</string>
|
||||
<string name="add_account__jmap_server_not_found">"Couldn't find the JMAP server from your email address"</string>
|
||||
</resources>
|
|
@ -1,47 +0,0 @@
|
|||
package com.fsck.k9
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.fsck.k9.ui.changelog.ChangeLogMode
|
||||
import com.fsck.k9.ui.changelog.ChangelogViewModel
|
||||
import com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity
|
||||
import com.fsck.k9.ui.endtoend.AutocryptKeyTransferPresenter
|
||||
import com.fsck.k9.ui.folders.FolderNameFormatter
|
||||
import com.fsck.k9.ui.helper.SizeFormatter
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.koin.core.annotation.KoinInternalApi
|
||||
import org.koin.core.logger.PrintLogger
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.java.KoinJavaComponent
|
||||
import org.koin.test.AutoCloseKoinTest
|
||||
import org.koin.test.check.checkModules
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.openintents.openpgp.OpenPgpApiManager
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = App::class)
|
||||
class DependencyInjectionTest : AutoCloseKoinTest() {
|
||||
val lifecycleOwner = mock<LifecycleOwner> {
|
||||
on { lifecycle } doReturn mock<Lifecycle>()
|
||||
}
|
||||
val autocryptTransferView = mock<AutocryptKeyTransferActivity>()
|
||||
|
||||
@KoinInternalApi
|
||||
@Test
|
||||
fun testDependencyTree() {
|
||||
KoinJavaComponent.getKoin().setupLogger(PrintLogger())
|
||||
|
||||
getKoin().checkModules {
|
||||
withParameter<OpenPgpApiManager> { lifecycleOwner }
|
||||
create<AutocryptKeyTransferPresenter> { parametersOf(lifecycleOwner, autocryptTransferView) }
|
||||
withParameter<FolderNameFormatter> { RuntimeEnvironment.application }
|
||||
withParameter<SizeFormatter> { RuntimeEnvironment.application }
|
||||
withParameter<ChangelogViewModel> { ChangeLogMode.CHANGE_LOG }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
mock-maker-inline
|
|
@ -1,5 +1,4 @@
|
|||
include ':app:k9mail'
|
||||
include ':app:k9mail-jmap'
|
||||
include ':app:ui:base'
|
||||
include ':app:ui:setup'
|
||||
include ':app:ui:legacy'
|
||||
|
|
Loading…
Reference in a new issue